Merge remote-tracking branch 'origin/caf/srcforge-ebtables/master'

* origin/caf/srcforge-ebtables/master: (791 commits)
  add RARP and update iana url
  bump release and update program date
  changelog for 2.0.10-4
  bugfix: setting rule in first built-in chain caused setting all counters to zero (thanks to James Sinclair)
  typo
  add changelog for v 2.0.10-3
  bump version
  fix rule counter bug (reported by  James Sinclair): possible wrong initialization of counters
  add changelog for v2.0.10-2
  respect LDFLAGS (Peter Volkov)
  enable compiler optimizations, bump release number
  small changes to remove warnings when optimization is on (thanks to Peter Volkov)
  initialize some variables to get rid of warnings when compiling with optimization
  remove definition of __EXPORTED_HEADERS__: we use sanitized header files now
  add changelog for v2.0.10-1
  bump prog version, add LOCKDIR variable, add -Werror compile flag
  remove compile warning, conditionally define LOCKFILE
  define __EXPORTED_HEADERS__ to get access to kernel headers
  add a reference to the lock file
  add info about the lock file compile time option
  ...
diff --git a/br-nf-bds/README b/br-nf-bds/README
new file mode 100644
index 0000000..bd06dcc
--- /dev/null
+++ b/br-nf-bds/README
@@ -0,0 +1,34 @@
+---
+These patches differ from Lennert's patches because I don't agree with
+Lennert's. Don't worry, mine are better ;)
+
+Date of first branch: April 27, 2002
+
+Changes in the policy will be mentioned in this file.
+
+Bart De Schuymer,
+June 1, 2002
+---
+All kernel code in linux/ is tagged like this example:
+br_nf_bds-0-0-8
+
+June 1, 2002
+---
+All kernel code in linux/ is tagged like this example:
+br_nf_bds-0-0-8-3
+All kernel code in linux2.5/ is tagged like this example:
+br_nf_bds-0-0-8-3-dev
+
+the -3 means it is the third patch against a kernel, but no functionality
+changed.
+
+August 31, 2002
+---
+bridge-nf is in the 2.5.xx series now. The only 2.5 file maintained in
+this repository is br_netfilter.c. No more patch screw-ups!
+
+The bridge-nf patch for 2.4.xx is no longer my concern.
+
+So, just use the latest 2.5 kernel and don't bother with this repository.
+
+September 02, 2002
diff --git a/br-nf-bds/linux/include/linux/netfilter.h b/br-nf-bds/linux/include/linux/netfilter.h
new file mode 100644
index 0000000..3ae5a64
--- /dev/null
+++ b/br-nf-bds/linux/include/linux/netfilter.h
@@ -0,0 +1,189 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/if.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#endif
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_MAX_VERDICT NF_REPEAT
+
+/* Generic cache responses from hook functions. */
+#define NFC_ALTERED 0x8000
+#define NFC_UNKNOWN 0x4000
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#ifdef CONFIG_NETFILTER
+
+extern void netfilter_init(void);
+
+/* Largest hook number + 1 */
+#define NF_MAX_HOOKS 8
+
+struct sk_buff;
+struct net_device;
+
+typedef unsigned int nf_hookfn(unsigned int hooknum,
+			       struct sk_buff **skb,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       int (*okfn)(struct sk_buff *));
+
+struct nf_hook_ops
+{
+	struct list_head list;
+
+	/* User fills in from here down. */
+	nf_hookfn *hook;
+	int pf;
+	int hooknum;
+	/* Hooks are ordered in ascending priority. */
+	int priority;
+};
+
+struct nf_sockopt_ops
+{
+	struct list_head list;
+
+	int pf;
+
+	/* Non-inclusive ranges: use 0/0/NULL to never get called. */
+	int set_optmin;
+	int set_optmax;
+	int (*set)(struct sock *sk, int optval, void *user, unsigned int len);
+
+	int get_optmin;
+	int get_optmax;
+	int (*get)(struct sock *sk, int optval, void *user, int *len);
+
+	/* Number of users inside set() or get(). */
+	unsigned int use;
+	struct task_struct *cleanup_task;
+};
+
+/* Each queued (to userspace) skbuff has one of these. */
+struct nf_info
+{
+	/* The ops struct which sent us to userspace. */
+	struct nf_hook_ops *elem;
+	
+	/* If we're sent to userspace, this keeps housekeeping info */
+	int pf;
+	unsigned int hook;
+	struct net_device *indev, *outdev;
+	int (*okfn)(struct sk_buff *);
+};
+                                                                                
+/* Function to register/unregister hook points. */
+int nf_register_hook(struct nf_hook_ops *reg);
+void nf_unregister_hook(struct nf_hook_ops *reg);
+
+/* Functions to register get/setsockopt ranges (non-inclusive).  You
+   need to check permissions yourself! */
+int nf_register_sockopt(struct nf_sockopt_ops *reg);
+void nf_unregister_sockopt(struct nf_sockopt_ops *reg);
+
+extern struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
+
+/* Activate hook; either okfn or kfree_skb called, unless a hook
+   returns NF_STOLEN (in which case, it's up to the hook to deal with
+   the consequences).
+
+   Returns -ERRNO if packet dropped.  Zero means queued, stolen or
+   accepted.
+*/
+
+/* RR:
+   > I don't want nf_hook to return anything because people might forget
+   > about async and trust the return value to mean "packet was ok".
+
+   AK:
+   Just document it clearly, then you can expect some sense from kernel
+   coders :)
+*/
+
+/* This is gross, but inline doesn't cut it for avoiding the function
+   call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+#ifdef CONFIG_NETFILTER_DEBUG
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_THRESH nf_hook_slow
+#else
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+(list_empty(&nf_hooks[(pf)][(hook)])					\
+ ? (okfn)(skb)								\
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
+#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
+(list_empty(&nf_hooks[(pf)][(hook)])					\
+ ? (okfn)(skb)								\
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+#endif
+
+int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+		 struct net_device *indev, struct net_device *outdev,
+		 int (*okfn)(struct sk_buff *), int thresh);
+
+/* Call setsockopt() */
+int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+		  int len);
+int nf_getsockopt(struct sock *sk, int pf, int optval, char *opt,
+		  int *len);
+
+/* Packet queuing */
+typedef int (*nf_queue_outfn_t)(struct sk_buff *skb, 
+                                struct nf_info *info, void *data);
+extern int nf_register_queue_handler(int pf, 
+                                     nf_queue_outfn_t outfn, void *data);
+extern int nf_unregister_queue_handler(int pf);
+extern void nf_reinject(struct sk_buff *skb,
+			struct nf_info *info,
+			unsigned int verdict);
+
+extern void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+extern void nf_dump_skb(int pf, struct sk_buff *skb);
+#endif
+
+/* FIXME: Before cache is ever used, this must be implemented for real. */
+extern void nf_invalidate_cache(int pf);
+
+#else /* !CONFIG_NETFILTER */
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
+#endif /*CONFIG_NETFILTER*/
+
+/* From arch/i386/kernel/smp.c:
+ *
+ *	Why isn't this somewhere standard ??
+ *
+ * Maybe because this procedure is horribly buggy, and does
+ * not deserve to live.  Think about signedness issues for five
+ * seconds to see why.		- Linus
+ */
+
+/* Two signed, return a signed. */
+#define SMAX(a,b) ((ssize_t)(a)>(ssize_t)(b) ? (ssize_t)(a) : (ssize_t)(b))
+#define SMIN(a,b) ((ssize_t)(a)<(ssize_t)(b) ? (ssize_t)(a) : (ssize_t)(b))
+
+/* Two unsigned, return an unsigned. */
+#define UMAX(a,b) ((size_t)(a)>(size_t)(b) ? (size_t)(a) : (size_t)(b))
+#define UMIN(a,b) ((size_t)(a)<(size_t)(b) ? (size_t)(a) : (size_t)(b))
+
+/* Two unsigned, return a signed. */
+#define SUMAX(a,b) ((size_t)(a)>(size_t)(b) ? (ssize_t)(a) : (ssize_t)(b))
+#define SUMIN(a,b) ((size_t)(a)<(size_t)(b) ? (ssize_t)(a) : (ssize_t)(b))
+#endif /*__KERNEL__*/
+
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/br-nf-bds/linux/include/linux/netfilter_ipv4.h b/br-nf-bds/linux/include/linux/netfilter_ipv4.h
new file mode 100644
index 0000000..946190a
--- /dev/null
+++ b/br-nf-bds/linux/include/linux/netfilter_ipv4.h
@@ -0,0 +1,80 @@
+#ifndef __LINUX_IP_NETFILTER_H
+#define __LINUX_IP_NETFILTER_H
+
+/* IPv4-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ */
+
+#include <linux/config.h>
+#include <linux/netfilter.h>
+
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP_SRC		0x0001
+/* Dest IP address. */
+#define NFC_IP_DST		0x0002
+/* Input device. */
+#define NFC_IP_IF_IN		0x0004
+/* Output device. */
+#define NFC_IP_IF_OUT		0x0008
+/* TOS. */
+#define NFC_IP_TOS		0x0010
+/* Protocol. */
+#define NFC_IP_PROTO		0x0020
+/* IP options. */
+#define NFC_IP_OPTIONS		0x0040
+/* Frag & flags. */
+#define NFC_IP_FRAG		0x0080
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP_TCPFLAGS		0x0100
+/* Source port. */
+#define NFC_IP_SRC_PT		0x0200
+/* Dest port. */
+#define NFC_IP_DST_PT		0x0400
+/* Something else about the proto */
+#define NFC_IP_PROTO_UNKNOWN	0x2000
+
+/* IP Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_IP_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_IP_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_IP_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_IP_POST_ROUTING	4
+#define NF_IP_NUMHOOKS		5
+
+enum nf_ip_hook_priorities {
+	NF_IP_PRI_FIRST = INT_MIN,
+	NF_IP_PRI_CONNTRACK = -200,
+	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+	NF_IP_PRI_MANGLE = -150,
+	NF_IP_PRI_NAT_DST = -100,
+	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+	NF_IP_PRI_FILTER = 0,
+	NF_IP_PRI_NAT_SRC = 100,
+	NF_IP_PRI_LAST = INT_MAX,
+};
+
+/* Arguments for setsockopt SOL_IP: */
+/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
+/* 2.2 firewalling (+ masq) went from 64 through 76 */
+/* 2.4 firewalling went 64 through 67. */
+#define SO_ORIGINAL_DST 80
+
+#ifdef __KERNEL__
+#ifdef CONFIG_NETFILTER_DEBUG
+void nf_debug_ip_local_deliver(struct sk_buff *skb);
+void nf_debug_ip_loopback_xmit(struct sk_buff *newskb);
+void nf_debug_ip_finish_output2(struct sk_buff *skb);
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+extern int ip_route_me_harder(struct sk_buff **pskb);
+#endif /*__KERNEL__*/
+
+#endif /*__LINUX_IP_NETFILTER_H*/
diff --git a/br-nf-bds/linux/include/linux/skbuff.h b/br-nf-bds/linux/include/linux/skbuff.h
new file mode 100644
index 0000000..4bc7c3f
--- /dev/null
+++ b/br-nf-bds/linux/include/linux/skbuff.h
@@ -0,0 +1,1152 @@
+/*
+ *	Definitions for the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+ 
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/cache.h>
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+
+#define HAVE_ALLOC_SKB		/* For the drivers to know */
+#define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
+#define SLAB_SKB 		/* Slabified skbuffs 	   */
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_HW 1
+#define CHECKSUM_UNNECESSARY 2
+
+#define SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES-1)) & ~(SMP_CACHE_BYTES-1))
+#define SKB_MAX_ORDER(X,ORDER)	(((PAGE_SIZE<<(ORDER)) - (X) - sizeof(struct skb_shared_info))&~(SMP_CACHE_BYTES-1))
+#define SKB_MAX_HEAD(X)		(SKB_MAX_ORDER((X),0))
+#define SKB_MAX_ALLOC		(SKB_MAX_ORDER(0,2))
+
+/* A. Checksumming of received packets by device.
+ *
+ *	NONE: device failed to checksum this packet.
+ *		skb->csum is undefined.
+ *
+ *	UNNECESSARY: device parsed packet and wouldbe verified checksum.
+ *		skb->csum is undefined.
+ *	      It is bad option, but, unfortunately, many of vendors do this.
+ *	      Apparently with secret goal to sell you new device, when you
+ *	      will add new protocol to your host. F.e. IPv6. 8)
+ *
+ *	HW: the most generic way. Device supplied checksum of _all_
+ *	    the packet as seen by netif_rx in skb->csum.
+ *	    NOTE: Even if device supports only some protocols, but
+ *	    is able to produce some skb->csum, it MUST use HW,
+ *	    not UNNECESSARY.
+ *
+ * B. Checksumming on output.
+ *
+ *	NONE: skb is checksummed by protocol or csum is not required.
+ *
+ *	HW: device is required to csum packet as seen by hard_start_xmit
+ *	from skb->h.raw to the end and to record the checksum
+ *	at skb->h.raw+skb->csum.
+ *
+ *	Device must show its capabilities in dev->features, set
+ *	at device setup time.
+ *	NETIF_F_HW_CSUM	- it is clever device, it is able to checksum
+ *			  everything.
+ *	NETIF_F_NO_CSUM - loopback or reliable single hop media.
+ *	NETIF_F_IP_CSUM - device is dumb. It is able to csum only
+ *			  TCP/UDP over IPv4. Sigh. Vendors like this
+ *			  way by an unknown reason. Though, see comment above
+ *			  about CHECKSUM_UNNECESSARY. 8)
+ *
+ *	Any questions? No questions, good. 		--ANK
+ */
+
+#ifdef __i386__
+#define NET_CALLER(arg) (*(((void**)&arg)-1))
+#else
+#define NET_CALLER(arg) __builtin_return_address(0)
+#endif
+
+#ifdef CONFIG_NETFILTER
+struct nf_conntrack {
+	atomic_t use;
+	void (*destroy)(struct nf_conntrack *);
+};
+
+struct nf_ct_info {
+	struct nf_conntrack *master;
+};
+#endif
+
+struct sk_buff_head {
+	/* These two members must be first. */
+	struct sk_buff	* next;
+	struct sk_buff	* prev;
+
+	__u32		qlen;
+	spinlock_t	lock;
+};
+
+struct sk_buff;
+
+#define MAX_SKB_FRAGS 6
+
+typedef struct skb_frag_struct skb_frag_t;
+
+struct skb_frag_struct
+{
+	struct page *page;
+	__u16 page_offset;
+	__u16 size;
+};
+
+/* This data is invariant across clones and lives at
+ * the end of the header data, ie. at skb->end.
+ */
+struct skb_shared_info {
+	atomic_t	dataref;
+	unsigned int	nr_frags;
+	struct sk_buff	*frag_list;
+	skb_frag_t	frags[MAX_SKB_FRAGS];
+};
+
+struct sk_buff {
+	/* These two members must be first. */
+	struct sk_buff	* next;			/* Next buffer in list 				*/
+	struct sk_buff	* prev;			/* Previous buffer in list 			*/
+
+	struct sk_buff_head * list;		/* List we are on				*/
+	struct sock	*sk;			/* Socket we are owned by 			*/
+	struct timeval	stamp;			/* Time we arrived				*/
+	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
+	struct net_device	*physindev;     /* Physical device we arrived on                */
+	struct net_device	*physoutdev;    /* Physical device we will leave by             */
+
+	/* Transport layer header */
+	union
+	{
+		struct tcphdr	*th;
+		struct udphdr	*uh;
+		struct icmphdr	*icmph;
+		struct igmphdr	*igmph;
+		struct iphdr	*ipiph;
+		struct spxhdr	*spxh;
+		unsigned char	*raw;
+	} h;
+
+	/* Network layer header */
+	union
+	{
+		struct iphdr	*iph;
+		struct ipv6hdr	*ipv6h;
+		struct arphdr	*arph;
+		struct ipxhdr	*ipxh;
+		unsigned char	*raw;
+	} nh;
+  
+	/* Link layer header */
+	union 
+	{	
+	  	struct ethhdr	*ethernet;
+	  	unsigned char 	*raw;
+	} mac;
+
+	struct  dst_entry *dst;
+
+	/* 
+	 * This is the control buffer. It is free to use for every
+	 * layer. Please put your private variables there. If you
+	 * want to keep them across layers you have to do a skb_clone()
+	 * first. This is owned by whoever has the skb queued ATM.
+	 */ 
+	char		cb[48];	 
+
+	unsigned int 	len;			/* Length of actual data			*/
+ 	unsigned int 	data_len;
+	unsigned int	csum;			/* Checksum 					*/
+	unsigned char 	__unused,		/* Dead field, may be reused			*/
+			cloned, 		/* head may be cloned (check refcnt to be sure). */
+  			pkt_type,		/* Packet class					*/
+  			ip_summed;		/* Driver fed us an IP checksum			*/
+	__u32		priority;		/* Packet queueing priority			*/
+	atomic_t	users;			/* User count - see datagram.c,tcp.c 		*/
+	unsigned short	protocol;		/* Packet protocol from driver. 		*/
+	unsigned short	security;		/* Security level of packet			*/
+	unsigned int	truesize;		/* Buffer size 					*/
+
+	unsigned char	*head;			/* Head of buffer 				*/
+	unsigned char	*data;			/* Data head pointer				*/
+	unsigned char	*tail;			/* Tail pointer					*/
+	unsigned char 	*end;			/* End pointer					*/
+
+	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
+#ifdef CONFIG_NETFILTER
+	/* Can be used for communication between hooks. */
+        unsigned long	nfmark;
+	/* Cache info */
+	__u32		nfcache;
+	/* Associated connection, if any */
+	struct nf_ct_info *nfct;
+#ifdef CONFIG_NETFILTER_DEBUG
+        unsigned int nf_debug;
+#endif
+#endif /*CONFIG_NETFILTER*/
+
+#if defined(CONFIG_HIPPI)
+	union{
+		__u32	ifield;
+	} private;
+#endif
+
+#ifdef CONFIG_NET_SCHED
+       __u32           tc_index;               /* traffic control index */
+#endif
+};
+
+#define SK_WMEM_MAX	65535
+#define SK_RMEM_MAX	65535
+
+#ifdef __KERNEL__
+/*
+ *	Handling routines are only of interest to the kernel
+ */
+#include <linux/slab.h>
+
+#include <asm/system.h>
+
+extern void			__kfree_skb(struct sk_buff *skb);
+extern struct sk_buff *		alloc_skb(unsigned int size, int priority);
+extern void			kfree_skbmem(struct sk_buff *skb);
+extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
+extern struct sk_buff *		skb_copy(const struct sk_buff *skb, int priority);
+extern struct sk_buff *		pskb_copy(struct sk_buff *skb, int gfp_mask);
+extern int			pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask);
+extern struct sk_buff *		skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom);
+extern struct sk_buff *		skb_copy_expand(const struct sk_buff *skb, 
+						int newheadroom,
+						int newtailroom,
+						int priority);
+#define dev_kfree_skb(a)	kfree_skb(a)
+extern void	skb_over_panic(struct sk_buff *skb, int len, void *here);
+extern void	skb_under_panic(struct sk_buff *skb, int len, void *here);
+
+/* Internal */
+#define skb_shinfo(SKB)		((struct skb_shared_info *)((SKB)->end))
+
+/**
+ *	skb_queue_empty - check if a queue is empty
+ *	@list: queue head
+ *
+ *	Returns true if the queue is empty, false otherwise.
+ */
+ 
+static inline int skb_queue_empty(struct sk_buff_head *list)
+{
+	return (list->next == (struct sk_buff *) list);
+}
+
+/**
+ *	skb_get - reference buffer
+ *	@skb: buffer to reference
+ *
+ *	Makes another reference to a socket buffer and returns a pointer
+ *	to the buffer.
+ */
+ 
+static inline struct sk_buff *skb_get(struct sk_buff *skb)
+{
+	atomic_inc(&skb->users);
+	return skb;
+}
+
+/*
+ * If users==1, we are the only owner and are can avoid redundant
+ * atomic change.
+ */
+ 
+/**
+ *	kfree_skb - free an sk_buff
+ *	@skb: buffer to free
+ *
+ *	Drop a reference to the buffer and free it if the usage count has
+ *	hit zero.
+ */
+ 
+static inline void kfree_skb(struct sk_buff *skb)
+{
+	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+		__kfree_skb(skb);
+}
+
+/* Use this if you didn't touch the skb state [for fast switching] */
+static inline void kfree_skb_fast(struct sk_buff *skb)
+{
+	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+		kfree_skbmem(skb);	
+}
+
+/**
+ *	skb_cloned - is the buffer a clone
+ *	@skb: buffer to check
+ *
+ *	Returns true if the buffer was generated with skb_clone() and is
+ *	one of multiple shared copies of the buffer. Cloned buffers are
+ *	shared data so must not be written to under normal circumstances.
+ */
+
+static inline int skb_cloned(struct sk_buff *skb)
+{
+	return skb->cloned && atomic_read(&skb_shinfo(skb)->dataref) != 1;
+}
+
+/**
+ *	skb_shared - is the buffer shared
+ *	@skb: buffer to check
+ *
+ *	Returns true if more than one person has a reference to this
+ *	buffer.
+ */
+ 
+static inline int skb_shared(struct sk_buff *skb)
+{
+	return (atomic_read(&skb->users) != 1);
+}
+
+/** 
+ *	skb_share_check - check if buffer is shared and if so clone it
+ *	@skb: buffer to check
+ *	@pri: priority for memory allocation
+ *	
+ *	If the buffer is shared the buffer is cloned and the old copy
+ *	drops a reference. A new clone with a single reference is returned.
+ *	If the buffer is not shared the original buffer is returned. When
+ *	being called from interrupt status or with spinlocks held pri must
+ *	be GFP_ATOMIC.
+ *
+ *	NULL is returned on a memory allocation failure.
+ */
+ 
+static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
+{
+	if (skb_shared(skb)) {
+		struct sk_buff *nskb;
+		nskb = skb_clone(skb, pri);
+		kfree_skb(skb);
+		return nskb;
+	}
+	return skb;
+}
+
+
+/*
+ *	Copy shared buffers into a new sk_buff. We effectively do COW on
+ *	packets to handle cases where we have a local reader and forward
+ *	and a couple of other messy ones. The normal one is tcpdumping
+ *	a packet thats being forwarded.
+ */
+ 
+/**
+ *	skb_unshare - make a copy of a shared buffer
+ *	@skb: buffer to check
+ *	@pri: priority for memory allocation
+ *
+ *	If the socket buffer is a clone then this function creates a new
+ *	copy of the data, drops a reference count on the old copy and returns
+ *	the new copy with the reference count at 1. If the buffer is not a clone
+ *	the original buffer is returned. When called with a spinlock held or
+ *	from interrupt state @pri must be %GFP_ATOMIC
+ *
+ *	%NULL is returned on a memory allocation failure.
+ */
+ 
+static inline struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
+{
+	struct sk_buff *nskb;
+	if(!skb_cloned(skb))
+		return skb;
+	nskb=skb_copy(skb, pri);
+	kfree_skb(skb);		/* Free our shared copy */
+	return nskb;
+}
+
+/**
+ *	skb_peek
+ *	@list_: list to peek at
+ *
+ *	Peek an &sk_buff. Unlike most other operations you _MUST_
+ *	be careful with this one. A peek leaves the buffer on the
+ *	list and someone else may run off with it. You must hold
+ *	the appropriate locks or have a private queue to do this.
+ *
+ *	Returns %NULL for an empty list or a pointer to the head element.
+ *	The reference count is not incremented and the reference is therefore
+ *	volatile. Use with caution.
+ */
+ 
+static inline struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+	struct sk_buff *list = ((struct sk_buff *)list_)->next;
+	if (list == (struct sk_buff *)list_)
+		list = NULL;
+	return list;
+}
+
+/**
+ *	skb_peek_tail
+ *	@list_: list to peek at
+ *
+ *	Peek an &sk_buff. Unlike most other operations you _MUST_
+ *	be careful with this one. A peek leaves the buffer on the
+ *	list and someone else may run off with it. You must hold
+ *	the appropriate locks or have a private queue to do this.
+ *
+ *	Returns %NULL for an empty list or a pointer to the tail element.
+ *	The reference count is not incremented and the reference is therefore
+ *	volatile. Use with caution.
+ */
+
+static inline struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
+{
+	struct sk_buff *list = ((struct sk_buff *)list_)->prev;
+	if (list == (struct sk_buff *)list_)
+		list = NULL;
+	return list;
+}
+
+/**
+ *	skb_queue_len	- get queue length
+ *	@list_: list to measure
+ *
+ *	Return the length of an &sk_buff queue. 
+ */
+ 
+static inline __u32 skb_queue_len(struct sk_buff_head *list_)
+{
+	return(list_->qlen);
+}
+
+static inline void skb_queue_head_init(struct sk_buff_head *list)
+{
+	spin_lock_init(&list->lock);
+	list->prev = (struct sk_buff *)list;
+	list->next = (struct sk_buff *)list;
+	list->qlen = 0;
+}
+
+/*
+ *	Insert an sk_buff at the start of a list.
+ *
+ *	The "__skb_xxxx()" functions are the non-atomic ones that
+ *	can only be called with interrupts disabled.
+ */
+
+/**
+ *	__skb_queue_head - queue a buffer at the list head
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the start of a list. This function takes no locks
+ *	and you must therefore hold required locks before calling it.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */	
+ 
+static inline void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+	struct sk_buff *prev, *next;
+
+	newsk->list = list;
+	list->qlen++;
+	prev = (struct sk_buff *)list;
+	next = prev->next;
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev = newsk;
+	prev->next = newsk;
+}
+
+
+/**
+ *	skb_queue_head - queue a buffer at the list head
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the start of the list. This function takes the
+ *	list lock and can be used safely with other locking &sk_buff functions
+ *	safely.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */	
+
+static inline void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	__skb_queue_head(list, newsk);
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+/**
+ *	__skb_queue_tail - queue a buffer at the list tail
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the end of a list. This function takes no locks
+ *	and you must therefore hold required locks before calling it.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */	
+ 
+
+static inline void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+	struct sk_buff *prev, *next;
+
+	newsk->list = list;
+	list->qlen++;
+	next = (struct sk_buff *)list;
+	prev = next->prev;
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev = newsk;
+	prev->next = newsk;
+}
+
+/**
+ *	skb_queue_tail - queue a buffer at the list tail
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the tail of the list. This function takes the
+ *	list lock and can be used safely with other locking &sk_buff functions
+ *	safely.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */	
+
+static inline void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	__skb_queue_tail(list, newsk);
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+/**
+ *	__skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. This function does not take any locks
+ *	so must be used with appropriate locks held only. The head item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
+{
+	struct sk_buff *next, *prev, *result;
+
+	prev = (struct sk_buff *) list;
+	next = prev->next;
+	result = NULL;
+	if (next != prev) {
+		result = next;
+		next = next->next;
+		list->qlen--;
+		next->prev = prev;
+		prev->next = next;
+		result->next = NULL;
+		result->prev = NULL;
+		result->list = NULL;
+	}
+	return result;
+}
+
+/**
+ *	skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. The list lock is taken so the function
+ *	may be used safely with other locking list functions. The head item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+{
+	unsigned long flags;
+	struct sk_buff *result;
+
+	spin_lock_irqsave(&list->lock, flags);
+	result = __skb_dequeue(list);
+	spin_unlock_irqrestore(&list->lock, flags);
+	return result;
+}
+
+/*
+ *	Insert a packet on a list.
+ */
+
+static inline void __skb_insert(struct sk_buff *newsk,
+	struct sk_buff * prev, struct sk_buff *next,
+	struct sk_buff_head * list)
+{
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev = newsk;
+	prev->next = newsk;
+	newsk->list = list;
+	list->qlen++;
+}
+
+/**
+ *	skb_insert	-	insert a buffer
+ *	@old: buffer to insert before
+ *	@newsk: buffer to insert
+ *
+ *	Place a packet before a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+
+static inline void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&old->list->lock, flags);
+	__skb_insert(newsk, old->prev, old, old->list);
+	spin_unlock_irqrestore(&old->list->lock, flags);
+}
+
+/*
+ *	Place a packet after a given packet in a list.
+ */
+
+static inline void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	__skb_insert(newsk, old, old->next, old->list);
+}
+
+/**
+ *	skb_append	-	append a buffer
+ *	@old: buffer to insert after
+ *	@newsk: buffer to insert
+ *
+ *	Place a packet after a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls.
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+
+
+static inline void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&old->list->lock, flags);
+	__skb_append(old, newsk);
+	spin_unlock_irqrestore(&old->list->lock, flags);
+}
+
+/*
+ * remove sk_buff from list. _Must_ be called atomically, and with
+ * the list known..
+ */
+ 
+static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+{
+	struct sk_buff * next, * prev;
+
+	list->qlen--;
+	next = skb->next;
+	prev = skb->prev;
+	skb->next = NULL;
+	skb->prev = NULL;
+	skb->list = NULL;
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ *	skb_unlink	-	remove a buffer from a list
+ *	@skb: buffer to remove
+ *
+ *	Place a packet after a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls
+ *	
+ *	Works even without knowing the list it is sitting on, which can be 
+ *	handy at times. It also means that THE LIST MUST EXIST when you 
+ *	unlink. Thus a list must have its contents unlinked before it is
+ *	destroyed.
+ */
+
+static inline void skb_unlink(struct sk_buff *skb)
+{
+	struct sk_buff_head *list = skb->list;
+
+	if(list) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&list->lock, flags);
+		if(skb->list == list)
+			__skb_unlink(skb, skb->list);
+		spin_unlock_irqrestore(&list->lock, flags);
+	}
+}
+
+/* XXX: more streamlined implementation */
+
+/**
+ *	__skb_dequeue_tail - remove from the tail of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the tail of the list. This function does not take any locks
+ *	so must be used with appropriate locks held only. The tail item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
+{
+	struct sk_buff *skb = skb_peek_tail(list); 
+	if (skb)
+		__skb_unlink(skb, list);
+	return skb;
+}
+
+/**
+ *	skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. The list lock is taken so the function
+ *	may be used safely with other locking list functions. The tail item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
+{
+	unsigned long flags;
+	struct sk_buff *result;
+
+	spin_lock_irqsave(&list->lock, flags);
+	result = __skb_dequeue_tail(list);
+	spin_unlock_irqrestore(&list->lock, flags);
+	return result;
+}
+
+static inline int skb_is_nonlinear(const struct sk_buff *skb)
+{
+	return skb->data_len;
+}
+
+static inline int skb_headlen(const struct sk_buff *skb)
+{
+	return skb->len - skb->data_len;
+}
+
+#define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) out_of_line_bug(); } while (0)
+#define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) out_of_line_bug(); } while (0)
+#define SKB_LINEAR_ASSERT(skb) do { if (skb_is_nonlinear(skb)) out_of_line_bug(); } while (0)
+
+/*
+ *	Add data to an sk_buff
+ */
+ 
+static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
+{
+	unsigned char *tmp=skb->tail;
+	SKB_LINEAR_ASSERT(skb);
+	skb->tail+=len;
+	skb->len+=len;
+	return tmp;
+}
+
+/**
+ *	skb_put - add data to a buffer
+ *	@skb: buffer to use 
+ *	@len: amount of data to add
+ *
+ *	This function extends the used data area of the buffer. If this would
+ *	exceed the total buffer size the kernel will panic. A pointer to the
+ *	first byte of the extra data is returned.
+ */
+ 
+static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
+{
+	unsigned char *tmp=skb->tail;
+	SKB_LINEAR_ASSERT(skb);
+	skb->tail+=len;
+	skb->len+=len;
+	if(skb->tail>skb->end) {
+		skb_over_panic(skb, len, current_text_addr());
+	}
+	return tmp;
+}
+
+static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
+{
+	skb->data-=len;
+	skb->len+=len;
+	return skb->data;
+}
+
+/**
+ *	skb_push - add data to the start of a buffer
+ *	@skb: buffer to use 
+ *	@len: amount of data to add
+ *
+ *	This function extends the used data area of the buffer at the buffer
+ *	start. If this would exceed the total buffer headroom the kernel will
+ *	panic. A pointer to the first byte of the extra data is returned.
+ */
+
+static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+{
+	skb->data-=len;
+	skb->len+=len;
+	if(skb->data<skb->head) {
+		skb_under_panic(skb, len, current_text_addr());
+	}
+	return skb->data;
+}
+
+static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
+{
+	skb->len-=len;
+	if (skb->len < skb->data_len)
+		out_of_line_bug();
+	return 	skb->data+=len;
+}
+
+/**
+ *	skb_pull - remove data from the start of a buffer
+ *	@skb: buffer to use 
+ *	@len: amount of data to remove
+ *
+ *	This function removes data from the start of a buffer, returning
+ *	the memory to the headroom. A pointer to the next data in the buffer
+ *	is returned. Once the data has been pulled future pushes will overwrite
+ *	the old data.
+ */
+
+static inline unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
+{	
+	if (len > skb->len)
+		return NULL;
+	return __skb_pull(skb,len);
+}
+
+extern unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta);
+
+static inline char *__pskb_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len > skb_headlen(skb) &&
+	    __pskb_pull_tail(skb, len-skb_headlen(skb)) == NULL)
+		return NULL;
+	skb->len -= len;
+	return 	skb->data += len;
+}
+
+static inline unsigned char * pskb_pull(struct sk_buff *skb, unsigned int len)
+{	
+	if (len > skb->len)
+		return NULL;
+	return __pskb_pull(skb,len);
+}
+
+static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len <= skb_headlen(skb))
+		return 1;
+	if (len > skb->len)
+		return 0;
+	return (__pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL);
+}
+
+/**
+ *	skb_headroom - bytes at buffer head
+ *	@skb: buffer to check
+ *
+ *	Return the number of bytes of free space at the head of an &sk_buff.
+ */
+ 
+static inline int skb_headroom(const struct sk_buff *skb)
+{
+	return skb->data-skb->head;
+}
+
+/**
+ *	skb_tailroom - bytes at buffer end
+ *	@skb: buffer to check
+ *
+ *	Return the number of bytes of free space at the tail of an sk_buff
+ */
+
+static inline int skb_tailroom(const struct sk_buff *skb)
+{
+	return skb_is_nonlinear(skb) ? 0 : skb->end-skb->tail;
+}
+
+/**
+ *	skb_reserve - adjust headroom
+ *	@skb: buffer to alter
+ *	@len: bytes to move
+ *
+ *	Increase the headroom of an empty &sk_buff by reducing the tail
+ *	room. This is only allowed for an empty buffer.
+ */
+
+static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
+{
+	skb->data+=len;
+	skb->tail+=len;
+}
+
+extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc);
+
+static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (!skb->data_len) {
+		skb->len = len;
+		skb->tail = skb->data+len;
+	} else {
+		___pskb_trim(skb, len, 0);
+	}
+}
+
+/**
+ *	skb_trim - remove end from a buffer
+ *	@skb: buffer to alter
+ *	@len: new length
+ *
+ *	Cut the length of a buffer down by removing data from the tail. If
+ *	the buffer is already under the length specified it is not modified.
+ */
+
+static inline void skb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (skb->len > len) {
+		__skb_trim(skb, len);
+	}
+}
+
+
+static inline int __pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (!skb->data_len) {
+		skb->len = len;
+		skb->tail = skb->data+len;
+		return 0;
+	} else {
+		return ___pskb_trim(skb, len, 1);
+	}
+}
+
+static inline int pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (len < skb->len)
+		return __pskb_trim(skb, len);
+	return 0;
+}
+
+/**
+ *	skb_orphan - orphan a buffer
+ *	@skb: buffer to orphan
+ *
+ *	If a buffer currently has an owner then we call the owner's
+ *	destructor function and make the @skb unowned. The buffer continues
+ *	to exist but is no longer charged to its former owner.
+ */
+
+
+static inline void skb_orphan(struct sk_buff *skb)
+{
+	if (skb->destructor)
+		skb->destructor(skb);
+	skb->destructor = NULL;
+	skb->sk = NULL;
+}
+
+/**
+ *	skb_purge - empty a list
+ *	@list: list to empty
+ *
+ *	Delete all buffers on an &sk_buff list. Each buffer is removed from
+ *	the list and one reference dropped. This function takes the list
+ *	lock and is atomic with respect to other list locking functions.
+ */
+
+
+static inline void skb_queue_purge(struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	while ((skb=skb_dequeue(list))!=NULL)
+		kfree_skb(skb);
+}
+
+/**
+ *	__skb_purge - empty a list
+ *	@list: list to empty
+ *
+ *	Delete all buffers on an &sk_buff list. Each buffer is removed from
+ *	the list and one reference dropped. This function does not take the
+ *	list lock and the caller must hold the relevant locks to use it.
+ */
+
+
+static inline void __skb_queue_purge(struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	while ((skb=__skb_dequeue(list))!=NULL)
+		kfree_skb(skb);
+}
+
+/**
+ *	__dev_alloc_skb - allocate an skbuff for sending
+ *	@length: length to allocate
+ *	@gfp_mask: get_free_pages mask, passed to alloc_skb
+ *
+ *	Allocate a new &sk_buff and assign it a usage count of one. The
+ *	buffer has unspecified headroom built in. Users should allocate
+ *	the headroom they think they need without accounting for the
+ *	built in space. The built in space is used for optimisations.
+ *
+ *	%NULL is returned in there is no free memory.
+ */
+ 
+static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
+					      int gfp_mask)
+{
+	struct sk_buff *skb;
+
+	skb = alloc_skb(length+16, gfp_mask);
+	if (skb)
+		skb_reserve(skb,16);
+	return skb;
+}
+
+/**
+ *	dev_alloc_skb - allocate an skbuff for sending
+ *	@length: length to allocate
+ *
+ *	Allocate a new &sk_buff and assign it a usage count of one. The
+ *	buffer has unspecified headroom built in. Users should allocate
+ *	the headroom they think they need without accounting for the
+ *	built in space. The built in space is used for optimisations.
+ *
+ *	%NULL is returned in there is no free memory. Although this function
+ *	allocates memory it can be called from an interrupt.
+ */
+ 
+static inline struct sk_buff *dev_alloc_skb(unsigned int length)
+{
+	return __dev_alloc_skb(length, GFP_ATOMIC);
+}
+
+/**
+ *	skb_cow - copy header of skb when it is required
+ *	@skb: buffer to cow
+ *	@headroom: needed headroom
+ *
+ *	If the skb passed lacks sufficient headroom or its data part
+ *	is shared, data is reallocated. If reallocation fails, an error
+ *	is returned and original skb is not changed.
+ *
+ *	The result is skb with writable area skb->head...skb->tail
+ *	and at least @headroom of space at head.
+ */
+
+static inline int
+skb_cow(struct sk_buff *skb, unsigned int headroom)
+{
+	int delta = (headroom > 16 ? headroom : 16) - skb_headroom(skb);
+
+	if (delta < 0)
+		delta = 0;
+
+	if (delta || skb_cloned(skb))
+		return pskb_expand_head(skb, (delta+15)&~15, 0, GFP_ATOMIC);
+	return 0;
+}
+
+/**
+ *	skb_linearize - convert paged skb to linear one
+ *	@skb: buffer to linarize
+ *	@gfp: allocation mode
+ *
+ *	If there is no free memory -ENOMEM is returned, otherwise zero
+ *	is returned and the old skb data released.  */
+int skb_linearize(struct sk_buff *skb, int gfp);
+
+static inline void *kmap_skb_frag(const skb_frag_t *frag)
+{
+#ifdef CONFIG_HIGHMEM
+	if (in_irq())
+		out_of_line_bug();
+
+	local_bh_disable();
+#endif
+	return kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ);
+}
+
+static inline void kunmap_skb_frag(void *vaddr)
+{
+	kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ);
+#ifdef CONFIG_HIGHMEM
+	local_bh_enable();
+#endif
+}
+
+#define skb_queue_walk(queue, skb) \
+		for (skb = (queue)->next;			\
+		     (skb != (struct sk_buff *)(queue));	\
+		     skb=skb->next)
+
+
+extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
+extern unsigned int		datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait);
+extern int			skb_copy_datagram(const struct sk_buff *from, int offset, char *to,int size);
+extern int			skb_copy_datagram_iovec(const struct sk_buff *from, int offset, struct iovec *to,int size);
+extern int			skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump);
+extern int			skb_copy_and_csum_datagram_iovec(const struct sk_buff *skb, int hlen, struct iovec *iov);
+extern void			skb_free_datagram(struct sock * sk, struct sk_buff *skb);
+
+extern unsigned int		skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum);
+extern int			skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len);
+extern unsigned int		skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum);
+extern void			skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
+
+extern void skb_init(void);
+extern void skb_add_mtu(int mtu);
+
+#ifdef CONFIG_NETFILTER
+static inline void
+nf_conntrack_put(struct nf_ct_info *nfct)
+{
+	if (nfct && atomic_dec_and_test(&nfct->master->use))
+		nfct->master->destroy(nfct->master);
+}
+static inline void
+nf_conntrack_get(struct nf_ct_info *nfct)
+{
+	if (nfct)
+		atomic_inc(&nfct->master->use);
+}
+#endif
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_SKBUFF_H */
diff --git a/br-nf-bds/linux/net/bridge/Makefile b/br-nf-bds/linux/net/bridge/Makefile
new file mode 100644
index 0000000..cbeca2a
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the IEEE 802.1d ethernet bridging layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+export-objs := br.o
+
+O_TARGET	:= bridge.o
+obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+			br_stp_if.o br_stp_timer.o br_netfilter.o
+obj-m		:= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/br-nf-bds/linux/net/bridge/br.c b/br-nf-bds/linux/net/bridge/br.c
new file mode 100644
index 0000000..8fbde85
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br.c
@@ -0,0 +1,86 @@
+/*
+ *	Generic parts
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br.c,v 1.3 2002/09/17 21:42:06 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/if_bridge.h>
+#include <asm/uaccess.h>
+#include "br_private.h"
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "../atm/lec.h"
+#endif
+
+int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+
+void br_dec_use_count()
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void br_inc_use_count()
+{
+	MOD_INC_USE_COUNT;
+}
+
+static int __init br_init(void)
+{
+	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+
+	if (br_netfilter_init())
+		return 1;
+	br_handle_frame_hook = br_handle_frame;
+	br_ioctl_hook = br_ioctl_deviceless_stub;
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = br_fdb_get;
+	br_fdb_put_hook = br_fdb_put;
+#endif
+	register_netdevice_notifier(&br_device_notifier);
+
+	return 0;
+}
+
+static void __br_clear_frame_hook(void)
+{
+	br_handle_frame_hook = NULL;
+}
+
+static void __br_clear_ioctl_hook(void)
+{
+	br_ioctl_hook = NULL;
+}
+
+static void __exit br_deinit(void)
+{
+	br_netfilter_fini();
+	unregister_netdevice_notifier(&br_device_notifier);
+	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+	net_call_rx_atomic(__br_clear_frame_hook);
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = NULL;
+	br_fdb_put_hook = NULL;
+#endif
+}
+
+EXPORT_SYMBOL(br_should_route_hook);
+
+module_init(br_init)
+module_exit(br_deinit)
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/linux/net/bridge/br_forward.c b/br-nf-bds/linux/net/bridge/br_forward.c
new file mode 100644
index 0000000..6f38e7e
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_forward.c
@@ -0,0 +1,151 @@
+/*
+ *	Forwarding decision
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_forward.c,v 1.4 2002/09/17 21:44:16 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
+{
+	if (skb->dev == p->dev ||
+	    p->state != BR_STATE_FORWARDING)
+		return 0;
+
+	return 1;
+}
+
+int br_dev_queue_push_xmit(struct sk_buff *skb)
+{
+	skb_push(skb, ETH_HLEN);
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+int br_forward_finish(struct sk_buff *skb)
+{
+	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+			br_dev_queue_push_xmit);
+
+	return 0;
+}
+
+static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	skb->dev = to->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			br_forward_finish);
+}
+
+static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	indev = skb->dev;
+	skb->dev = to->dev;
+
+	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+			br_forward_finish);
+}
+
+/* called under bridge lock */
+void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_deliver(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_forward(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
+	void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
+{
+	struct net_bridge_port *p;
+	struct net_bridge_port *prev;
+
+	if (clone) {
+		struct sk_buff *skb2;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			br->statistics.tx_dropped++;
+			return;
+		}
+
+		skb = skb2;
+	}
+
+	prev = NULL;
+
+	p = br->port_list;
+	while (p != NULL) {
+		if (should_deliver(p, skb)) {
+			if (prev != NULL) {
+				struct sk_buff *skb2;
+
+				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+					br->statistics.tx_dropped++;
+					kfree_skb(skb);
+					return;
+				}
+
+				__packet_hook(prev, skb2);
+			}
+
+			prev = p;
+		}
+
+		p = p->next;
+	}
+
+	if (prev != NULL) {
+		__packet_hook(prev, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_deliver);
+}
+
+/* called under bridge lock */
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_forward);
+}
diff --git a/br-nf-bds/linux/net/bridge/br_input.c b/br-nf-bds/linux/net/bridge/br_input.c
new file mode 100644
index 0000000..9365f9e
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_input.c
@@ -0,0 +1,179 @@
+/*
+ *	Handle incoming frames
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_input.c,v 1.5 2002/09/29 19:00:11 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+static int br_pass_frame_up_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	netif_rx(skb);
+
+	return 0;
+}
+
+static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	br->statistics.rx_packets++;
+	br->statistics.rx_bytes += skb->len;
+
+	indev = skb->dev;
+	skb->dev = &br->dev;
+	skb->pkt_type = PACKET_HOST;
+	skb_push(skb, ETH_HLEN);
+	skb->protocol = eth_type_trans(skb, &br->dev);
+
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
+			br_pass_frame_up_finish);
+}
+
+int br_handle_frame_finish(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_fdb_entry *dst;
+	struct net_bridge_port *p;
+	int passedup;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	passedup = 0;
+	if (br->dev.flags & IFF_PROMISC) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 != NULL) {
+			passedup = 1;
+			br_pass_frame_up(br, skb2);
+		}
+	}
+
+	if (dest[0] & 1) {
+		br_flood_forward(br, skb, !passedup);
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		goto out;
+	}
+
+	dst = br_fdb_get(br, dest);
+	if (dst != NULL && dst->is_local) {
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		else
+			kfree_skb(skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	if (dst != NULL) {
+		br_forward(dst->dst, skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	br_flood_forward(br, skb, 0);
+
+out:
+	read_unlock(&br->lock);
+	return 0;
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+}
+
+int br_handle_frame(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_port *p;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	if (!(br->dev.flags & IFF_UP) ||
+	    p->state == BR_STATE_DISABLED)
+		goto err;
+
+	if (skb->mac.ethernet->h_source[0] & 1)
+		goto err;
+
+	if (p->state == BR_STATE_LEARNING ||
+	    p->state == BR_STATE_FORWARDING)
+		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+	if (br->stp_enabled &&
+	    !memcmp(dest, bridge_ula, 5) &&
+	    !(dest[5] & 0xF0))
+		goto handle_special_frame;
+
+	if (p->state == BR_STATE_FORWARDING) {
+		if (br_should_route_hook && br_should_route_hook(&skb)) {
+			read_unlock(&br->lock);
+			return -1;
+		}
+
+		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+			br_handle_frame_finish);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+
+handle_special_frame:
+	if (!dest[5]) {
+		br_stp_handle_bpdu(skb);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+	read_unlock(&br->lock);
+	kfree_skb(skb);
+	return 0;
+}
diff --git a/br-nf-bds/linux/net/bridge/br_netfilter.c b/br-nf-bds/linux/net/bridge/br_netfilter.c
new file mode 100644
index 0000000..68e3267
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_netfilter.c
@@ -0,0 +1,610 @@
+/*
+ *	Handle firewalling
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek               <buytenh@gnu.org>
+ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
+ *
+ *	$Id: br_netfilter.c,v 1.3 2002/09/11 17:41:38 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/in_route.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include "br_private.h"
+
+
+#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
+/* As the original source/destination addresses are variables private to this
+ * file, we store them in unused space at the end of the control buffer.
+ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
+ * of space at the end, so that fits.  Usage of the original source address
+ * and the original destination address never overlaps (daddr is needed
+ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
+ * well.
+ */
+#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
+
+#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
+#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
+#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
+#else
+#define store_orig_dstaddr(skb)
+#define store_orig_srcaddr(skb)
+#define dnat_took_place(skb)		(0)
+#define snat_took_place(skb)		(0)
+#endif
+
+
+#define has_bridge_parent(device)	((device)->br_port != NULL)
+#define bridge_parent(device)		(&((device)->br_port->br->dev))
+
+
+/* As opposed to the DNAT case, for the SNAT case it's not quite
+ * clear what we should do with ethernet addresses in NAT'ed
+ * packets.  Use this heuristic for now.
+ */
+static inline void __maybe_fixup_src_address(struct sk_buff *skb)
+{
+	if (snat_took_place(skb) &&
+	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
+		memcpy(skb->mac.ethernet->h_source,
+			bridge_parent(skb->dev)->dev_addr,
+			ETH_ALEN);
+	}
+}
+
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it.  Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static struct net_device __fake_net_device = {
+	hard_header_len:	ETH_HLEN
+};
+
+static struct rtable __fake_rtable = {
+	u: {
+		dst: {
+			__refcnt:		ATOMIC_INIT(1),
+			dev:			&__fake_net_device,
+			pmtu:			1500
+		}
+	},
+
+	rt_flags:	0
+};
+
+
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static void __br_dnat_complain(void)
+{
+	static unsigned long last_complaint = 0;
+
+	if (jiffies - last_complaint >= 5 * HZ) {
+		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
+			"forwarding to be enabled\n");
+		last_complaint = jiffies;
+	}
+}
+
+
+static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+{
+	skb->dev = bridge_parent(skb->dev);
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
+#endif
+	skb->dst->output(skb);
+	return 0;
+}
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#define __br_handle_frame_finish  br_nf_pre_routing_finish_route
+static inline int br_nf_pre_routing_finish_route(struct sk_buff *skb)
+{
+	skb->nf_debug = 0;
+	br_handle_frame_finish(skb);
+	return 0;
+}
+#else
+#define __br_handle_frame_finish br_handle_frame_finish
+#endif
+
+/* This requires some explaining.  If DNAT has taken place,
+ * we will need to fix up the destination ethernet address,
+ * and this is a tricky process.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ *    port group as it was received on.  We can still bridge
+ *    the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ *    a non-bridged device or another bridge port group.
+ *    The packet will need to be routed.
+ *
+ * The way to distinguish between the two is by calling ip_route_input()
+ * and looking at skb->dst->dev, which it changed to the destination device
+ * if ip_route_input() succeeds.
+ *
+ * Let us first consider ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet came in on,
+ * we can consider this bridging. We then call skb->dst->output() which will
+ * make the packet enter br_nf_local_out() not much later. In that function
+ * it is assured that the iptables FORWARD chain is traversed for the packet.
+ *
+ * Else, the packet is considered to be routed and we just change the
+ * destination MAC address so that the packet will later be passed up to the ip
+ * stack to be routed.
+ *
+ * Let us now consider ip_route_input() fails:
+ *
+ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
+ * fail, while ip_route_output() will return success. The source address for
+ * ip_route_output() is set to zero, so ip_route_output()
+ * thinks we're handling a locally generated packet and won't care if
+ * ip forwarding is allowed. We send a warning message to the users's log
+ * telling her to put ip forwarding on.
+ *
+ * ip_route_input() will also fail if there is no route available. Then we just
+ * drop the packet.
+ *
+ * The other special thing happening here is putting skb->physoutdev on
+ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
+ * needed so that br_nf_local_out() can know that it has to give the packets to
+ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
+ * --Lennert, 20020411
+ * --Bart, 20020416 (updated)
+ */
+
+static int br_nf_pre_routing_finish(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	struct iphdr *iph = skb->nh.iph;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+#endif
+	if (dnat_took_place(skb)) {
+		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
+			struct rtable *rt;
+
+			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
+				// bridged dnated traffic isn't dependent on
+				// disabled ip_forwarding
+				if (((struct dst_entry *)rt)->dev == dev) {
+					skb->dst = (struct dst_entry *)rt;
+					goto bridged_dnat;
+				}
+				__br_dnat_complain();
+				dst_release((struct dst_entry *)rt);
+			}
+			kfree_skb(skb);
+			return 0;
+		} else {
+			if (skb->dst->dev == dev) {
+bridged_dnat:
+				// tell br_nf_local_out this is a bridged frame
+				skb->physoutdev = &__fake_net_device;
+				skb->dev = skb->physindev;
+				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+						br_nf_pre_routing_finish_bridge, 1);
+				return 0;
+			}
+			// tell br_nf_local_out this is a routed frame
+			skb->physoutdev = NULL;
+			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
+		}
+	} else {
+		skb->dst = (struct dst_entry *)&__fake_rtable;
+		dst_hold(skb->dst);
+	}
+	skb->dev = skb->physindev;
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+			__br_handle_frame_finish, 1);
+
+	return 0;
+}
+
+/* Replicate the checks that IPv4 does on packet reception.
+ * Set skb->dev to the bridge device (i.e. parent of the
+ * receiving device) to make netfilter happy, the REDIRECT
+ * target in particular.  Save the original destination IP
+ * address to be able to detect DNAT afterwards.
+ */
+static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	struct iphdr *iph;
+	__u32 len;
+	struct sk_buff *skb;
+
+	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+		goto out;
+
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto inhdr_error;
+
+	iph = skb->nh.iph;
+	if (iph->ihl < 5 || iph->version != 4)
+		goto inhdr_error;
+
+	if (!pskb_may_pull(skb, 4*iph->ihl))
+		goto inhdr_error;
+
+	iph = skb->nh.iph;
+	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+		goto inhdr_error;
+
+	len = ntohs(iph->tot_len);
+	if (skb->len < len || len < 4*iph->ihl)
+		goto inhdr_error;
+
+	if (skb->len > len) {
+		__pskb_trim(skb, len);
+		if (skb->ip_summed == CHECKSUM_HW)
+			skb->ip_summed = CHECKSUM_NONE;
+	}
+
+	skb->physindev = skb->dev;
+	skb->dev = bridge_parent(skb->dev);
+	if (skb->pkt_type == PACKET_OTHERHOST)
+		skb->pkt_type = PACKET_HOST;
+	store_orig_dstaddr(skb);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	(*pskb)->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+#endif
+	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+		br_nf_pre_routing_finish);
+
+	return NF_STOLEN;
+
+inhdr_error:
+//	IP_INC_STATS_BH(IpInHdrErrors);
+out:
+	return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one.  On the way up, the
+ * packet would pass through PRE_ROUTING again (which already
+ * took place when the packet entered the bridge), but we
+ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
+ * prevent this from happening.
+ */
+static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+		dst_release(skb->dst);
+		skb->dst = NULL;
+	}
+
+	return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf_forward_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
+			skb->dev, br_forward_finish, 1);
+
+	return 0;
+}
+
+/* This is the 'purely bridged' case.  We pass the packet to
+ * netfilter with indev and outdev set to the bridge device,
+ * but we are still able to filter on the 'real' indev/outdev
+ * because another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
+static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+
+	// don't mess with non-ip frames, also don't mess with the ip-packets
+	// when br_nf_local_out_finish explicitly says so.
+	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
+		return NF_ACCEPT;
+
+	skb->physoutdev = skb->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_IP_FORWARD);
+#endif
+	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
+			bridge_parent(skb->dev), br_nf_forward_finish);
+
+	return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT
+***********************************************/
+static int br_nf_local_out_finish_forward(struct sk_buff *skb)
+{
+	struct net_device *dev;
+
+	dev = skb->physindev;
+	// tell br_nf_forward to stay away
+	skb->physindev = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug &= ~(1 << NF_BR_FORWARD);
+#endif
+	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
+		br_forward_finish);
+
+	return 0;
+}
+
+static int br_nf_local_out_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
+#endif
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			br_forward_finish, INT_MIN + 1);
+
+	return 0;
+}
+
+
+/* This hook sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device).  For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
+ * and reinject them later, when we have determined the real
+ * output device.  This reinjecting happens here.
+ *
+ * If skb->physindev is NULL, the bridge-nf code never touched
+ * this packet before, and so the packet was locally originated.
+ * We call the IPv4 LOCAL_OUT hook.
+ *
+ * If skb->physindev isn't NULL, there are two cases:
+ * 1. The packet was IP routed.
+ * 2. The packet was cross-bridge DNAT'ed (see the comment near
+ *    PF_BRIDGE/PRE_ROUTING).
+ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
+ * if the packet originally came from a bridge device, and in
+ * case 2, skb->physindev will have a bridge device as parent,
+ * so we use that parent device as indev.  Otherwise, we just
+ * use physindev.
+ *
+ * If skb->physoutdev == NULL the bridge code never touched the
+ * packet or the packet was routed in br_nf_pre_routing_finish().
+ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
+ * If not, the packet is actually a bridged one so we give it to
+ * the NF_BR_FORWARD hook.
+ */
+
+static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
+{
+	int hookno, prio;
+	int (*okfn)(struct sk_buff *skb);
+	struct net_device *realindev;
+	struct sk_buff *skb = *pskb;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	/* Sometimes we get packets with NULL ->dst here (for example,
+	 * running a dhcp client daemon triggers this).
+	 */
+	if (skb->dst == NULL)
+		return NF_ACCEPT;
+
+	// bridged, take forward
+	// (see big note in front of br_nf_pre_routing_finish)
+	if (skb->physoutdev == &__fake_net_device) {
+		okfn = br_nf_local_out_finish_forward;
+	} else if (skb->physoutdev == NULL) {
+		// non-bridged: routed or locally generated traffic, take local_out
+		// (see big note in front of br_nf_pre_routing_finish)
+		okfn = br_nf_local_out_finish;
+	} else {
+		printk("ARGH: bridge_or_routed hack doesn't work\n");
+		okfn = br_nf_local_out_finish;
+	}
+
+	skb->physoutdev = skb->dev;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_LOCAL_OUT);
+#endif
+	hookno = NF_IP_LOCAL_OUT;
+	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
+	if ((realindev = skb->physindev) != NULL) {
+		hookno = NF_IP_FORWARD;
+		// there is an iptables mangle table FORWARD chain with
+		// priority -150. This chain should see the physical out-dev.
+		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
+		if (has_bridge_parent(realindev))
+			realindev = bridge_parent(realindev);
+	}
+
+	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
+			bridge_parent(skb->dev), okfn, prio + 1);
+
+	return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static int br_nf_post_routing_finish(struct sk_buff *skb)
+{
+	__maybe_fixup_src_address(skb);
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_POST_ROUTING);
+#endif
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
+			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
+
+	return 0;
+}
+
+static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+
+	/* Be very paranoid.  */
+	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
+		if (skb->dev != NULL) {
+			printk("[%s]", skb->dev->name);
+			if (has_bridge_parent(skb->dev))
+				printk("[%s]", bridge_parent(skb->dev)->name);
+		}
+		printk("\n");
+		return NF_ACCEPT;
+	}
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	/* Sometimes we get packets with NULL ->dst here (for example,
+	 * running a dhcp client daemon triggers this).
+	 */
+	if (skb->dst == NULL)
+		return NF_ACCEPT;
+
+	store_orig_srcaddr(skb);
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
+#endif
+	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+		bridge_parent(skb->dev), br_nf_post_routing_finish);
+
+	return NF_STOLEN;
+}
+
+
+/* IPv4/SABOTAGE *****************************************************/
+/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
+ * for the second time.  */
+static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	if (in->hard_start_xmit == br_dev_xmit &&
+	    okfn != br_nf_pre_routing_finish) {
+		okfn(*pskb);
+		return NF_STOLEN;
+	}
+
+	return NF_ACCEPT;
+}
+
+/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
+ * and PF_INET/POST_ROUTING until we have done the forwarding
+ * decision in the bridge code and have determined skb->physoutdev.
+ */
+static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	if (out->hard_start_xmit == br_dev_xmit &&
+	    okfn != br_nf_forward_finish &&
+	    okfn != br_nf_local_out_finish &&
+	    okfn != br_nf_post_routing_finish) {
+		struct sk_buff *skb = *pskb;
+
+		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
+			skb->physindev = (struct net_device *)in;
+		okfn(skb);
+		return NF_STOLEN;
+	}
+
+	return NF_ACCEPT;
+}
+
+
+static struct nf_hook_ops br_nf_ops[] = {
+	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
+	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
+	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
+	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
+	// get bridged traffic as input
+	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
+	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
+
+	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
+
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
+};
+
+#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
+
+
+int br_netfilter_init(void)
+{
+	int i;
+
+#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
+	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
+		extern int __too_little_space_in_control_buffer(void);
+		__too_little_space_in_control_buffer();
+	}
+#endif
+
+	for (i=0;i<NUMHOOKS;i++) {
+		int ret;
+
+		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
+			continue;
+
+		while (i--)
+			nf_unregister_hook(&br_nf_ops[i]);
+
+		return ret;
+	}
+
+	printk(KERN_NOTICE "Bridge firewalling registered\n");
+
+	return 0;
+}
+
+void br_netfilter_fini(void)
+{
+	int i;
+
+	for (i=NUMHOOKS-1;i>=0;i--)
+		nf_unregister_hook(&br_nf_ops[i]);
+}
diff --git a/br-nf-bds/linux/net/bridge/br_private.h b/br-nf-bds/linux/net/bridge/br_private.h
new file mode 100644
index 0000000..46cf249
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_private.h
@@ -0,0 +1,211 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_private.h,v 1.3 2002/08/24 16:23:34 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _BR_PRIVATE_H
+#define _BR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/if_bridge.h>
+#include "br_private_timer.h"
+
+#define BR_HASH_BITS 8
+#define BR_HASH_SIZE (1 << BR_HASH_BITS)
+
+#define BR_HOLD_TIME (1*HZ)
+
+typedef struct bridge_id bridge_id;
+typedef struct mac_addr mac_addr;
+typedef __u16 port_id;
+
+struct bridge_id
+{
+	unsigned char	prio[2];
+	unsigned char	addr[6];
+};
+
+struct mac_addr
+{
+	unsigned char	addr[6];
+	unsigned char	pad[2];
+};
+
+struct net_bridge_fdb_entry
+{
+	struct net_bridge_fdb_entry	*next_hash;
+	struct net_bridge_fdb_entry	**pprev_hash;
+	atomic_t			use_count;
+	mac_addr			addr;
+	struct net_bridge_port		*dst;
+	unsigned long			ageing_timer;
+	unsigned			is_local:1;
+	unsigned			is_static:1;
+};
+
+struct net_bridge_port
+{
+	struct net_bridge_port		*next;
+	struct net_bridge		*br;
+	struct net_device		*dev;
+	int				port_no;
+
+	/* STP */
+	port_id				port_id;
+	int				state;
+	int				path_cost;
+	bridge_id			designated_root;
+	int				designated_cost;
+	bridge_id			designated_bridge;
+	port_id				designated_port;
+	unsigned			topology_change_ack:1;
+	unsigned			config_pending:1;
+	int				priority;
+
+	struct br_timer			forward_delay_timer;
+	struct br_timer			hold_timer;
+	struct br_timer			message_age_timer;
+};
+
+struct net_bridge
+{
+	struct net_bridge		*next;
+	rwlock_t			lock;
+	struct net_bridge_port		*port_list;
+	struct net_device		dev;
+	struct net_device_stats		statistics;
+	rwlock_t			hash_lock;
+	struct net_bridge_fdb_entry	*hash[BR_HASH_SIZE];
+	struct timer_list		tick;
+
+	/* STP */
+	bridge_id			designated_root;
+	int				root_path_cost;
+	int				root_port;
+	int				max_age;
+	int				hello_time;
+	int				forward_delay;
+	bridge_id			bridge_id;
+	int				bridge_max_age;
+	int				bridge_hello_time;
+	int				bridge_forward_delay;
+	unsigned			stp_enabled:1;
+	unsigned			topology_change:1;
+	unsigned			topology_change_detected:1;
+
+	struct br_timer			hello_timer;
+	struct br_timer			tcn_timer;
+	struct br_timer			topology_change_timer;
+	struct br_timer			gc_timer;
+
+	int				ageing_time;
+	int				gc_interval;
+};
+
+extern struct notifier_block br_device_notifier;
+extern unsigned char bridge_ula[6];
+
+/* br.c */
+extern void br_dec_use_count(void);
+extern void br_inc_use_count(void);
+
+/* br_device.c */
+extern void br_dev_setup(struct net_device *dev);
+extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* br_fdb.c */
+extern void br_fdb_changeaddr(struct net_bridge_port *p,
+		       unsigned char *newaddr);
+extern void br_fdb_cleanup(struct net_bridge *br);
+extern void br_fdb_delete_by_port(struct net_bridge *br,
+			   struct net_bridge_port *p);
+extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
+					unsigned char *addr);
+extern void br_fdb_put(struct net_bridge_fdb_entry *ent);
+extern int  br_fdb_get_entries(struct net_bridge *br,
+			unsigned char *_buf,
+			int maxnum,
+			int offset);
+extern void br_fdb_insert(struct net_bridge *br,
+		   struct net_bridge_port *source,
+		   unsigned char *addr,
+		   int is_local);
+
+/* br_forward.c */
+extern void br_deliver(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+extern void br_forward(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern int br_forward_finish(struct sk_buff *skb);
+extern void br_flood_deliver(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+extern void br_flood_forward(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+
+/* br_if.c */
+extern int br_add_bridge(char *name);
+extern int br_del_bridge(char *name);
+extern int br_add_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_del_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_get_bridge_ifindices(int *indices,
+			    int num);
+extern void br_get_port_ifindices(struct net_bridge *br,
+			   int *ifindices);
+
+/* br_input.c */
+extern int br_handle_frame_finish(struct sk_buff *skb);
+extern int br_handle_frame(struct sk_buff *skb);
+
+/* br_ioctl.c */
+extern void br_call_ioctl_atomic(void (*fn)(void));
+extern int br_ioctl(struct net_bridge *br,
+	     unsigned int cmd,
+	     unsigned long arg0,
+	     unsigned long arg1,
+	     unsigned long arg2);
+extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_netfilter.c */
+extern int br_netfilter_init(void);
+extern void br_netfilter_fini(void);
+
+/* br_stp.c */
+extern int br_is_root_bridge(struct net_bridge *br);
+extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+				    int port_no);
+extern void br_init_port(struct net_bridge_port *p);
+extern port_id br_make_port_id(struct net_bridge_port *p);
+extern void br_become_designated_port(struct net_bridge_port *p);
+
+/* br_stp_if.c */
+extern void br_stp_enable_bridge(struct net_bridge *br);
+extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_enable_port(struct net_bridge_port *p);
+extern void br_stp_disable_port(struct net_bridge_port *p);
+extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
+extern void br_stp_set_bridge_priority(struct net_bridge *br,
+				int newprio);
+extern void br_stp_set_port_priority(struct net_bridge_port *p,
+			      int newprio);
+extern void br_stp_set_path_cost(struct net_bridge_port *p,
+			  int path_cost);
+
+/* br_stp_bpdu.c */
+extern void br_stp_handle_bpdu(struct sk_buff *skb);
+
+#endif
diff --git a/br-nf-bds/linux/net/core/netfilter.c b/br-nf-bds/linux/net/core/netfilter.c
new file mode 100644
index 0000000..198dad6
--- /dev/null
+++ b/br-nf-bds/linux/net/core/netfilter.c
@@ -0,0 +1,654 @@
+/* netfilter.c: look after the filters for various protocols. 
+ * Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
+ *
+ * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
+ * way.
+ *
+ * Rusty Russell (C)2000 -- This code is GPL.
+ *
+ * February 2000: Modified by James Morris to have 1 queue per protocol.
+ * 15-Mar-2000:   Added NF_REPEAT --RR.
+ */
+#include <linux/config.h>
+#include <linux/netfilter.h>
+#include <net/protocol.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/brlock.h>
+#include <linux/inetdevice.h>
+#include <net/sock.h>
+#include <net/route.h>
+#include <linux/ip.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* In this code, we can be waiting indefinitely for userspace to
+ * service a packet if a hook returns NF_QUEUE.  We could keep a count
+ * of skbuffs queued for userspace, and not deregister a hook unless
+ * this is zero, but that sucks.  Now, we simply check when the
+ * packets come back: if the hook is gone, the packet is discarded. */
+#ifdef CONFIG_NETFILTER_DEBUG
+#define NFDEBUG(format, args...)  printk(format , ## args)
+#else
+#define NFDEBUG(format, args...)
+#endif
+
+/* Sockopts only registered and called from user context, so
+   BR_NETPROTO_LOCK would be overkill.  Also, [gs]etsockopt calls may
+   sleep. */
+static DECLARE_MUTEX(nf_sockopt_mutex);
+
+struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
+static LIST_HEAD(nf_sockopts);
+
+/* 
+ * A queue handler may be registered for each protocol.  Each is protected by
+ * long term mutex.  The handler must provide an an outfn() to accept packets
+ * for queueing and must reinject all packets it receives, no matter what.
+ */
+static struct nf_queue_handler_t {
+	nf_queue_outfn_t outfn;
+	void *data;
+} queue_handler[NPROTO];
+
+int nf_register_hook(struct nf_hook_ops *reg)
+{
+	struct list_head *i;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	for (i = nf_hooks[reg->pf][reg->hooknum].next; 
+	     i != &nf_hooks[reg->pf][reg->hooknum]; 
+	     i = i->next) {
+		if (reg->priority < ((struct nf_hook_ops *)i)->priority)
+			break;
+	}
+	list_add(&reg->list, i->prev);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	return 0;
+}
+
+void nf_unregister_hook(struct nf_hook_ops *reg)
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	list_del(&reg->list);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+/* Do exclusive ranges overlap? */
+static inline int overlap(int min1, int max1, int min2, int max2)
+{
+	return max1 > min2 && min1 < max2;
+}
+
+/* Functions to register sockopt ranges (exclusive). */
+int nf_register_sockopt(struct nf_sockopt_ops *reg)
+{
+	struct list_head *i;
+	int ret = 0;
+
+	if (down_interruptible(&nf_sockopt_mutex) != 0)
+		return -EINTR;
+
+	for (i = nf_sockopts.next; i != &nf_sockopts; i = i->next) {
+		struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i;
+		if (ops->pf == reg->pf
+		    && (overlap(ops->set_optmin, ops->set_optmax, 
+				reg->set_optmin, reg->set_optmax)
+			|| overlap(ops->get_optmin, ops->get_optmax, 
+				   reg->get_optmin, reg->get_optmax))) {
+			NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
+				ops->set_optmin, ops->set_optmax, 
+				ops->get_optmin, ops->get_optmax, 
+				reg->set_optmin, reg->set_optmax,
+				reg->get_optmin, reg->get_optmax);
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	list_add(&reg->list, &nf_sockopts);
+out:
+	up(&nf_sockopt_mutex);
+	return ret;
+}
+
+void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
+{
+	/* No point being interruptible: we're probably in cleanup_module() */
+ restart:
+	down(&nf_sockopt_mutex);
+	if (reg->use != 0) {
+		/* To be woken by nf_sockopt call... */
+		/* FIXME: Stuart Young's name appears gratuitously. */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		reg->cleanup_task = current;
+		up(&nf_sockopt_mutex);
+		schedule();
+		goto restart;
+	}
+	list_del(&reg->list);
+	up(&nf_sockopt_mutex);
+}
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4.h>
+
+static void debug_print_hooks_ip(unsigned int nf_debug)
+{
+	if (nf_debug & (1 << NF_IP_PRE_ROUTING)) {
+		printk("PRE_ROUTING ");
+		nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+	}
+	if (nf_debug & (1 << NF_IP_LOCAL_IN)) {
+		printk("LOCAL_IN ");
+		nf_debug ^= (1 << NF_IP_LOCAL_IN);
+	}
+	if (nf_debug & (1 << NF_IP_FORWARD)) {
+		printk("FORWARD ");
+		nf_debug ^= (1 << NF_IP_FORWARD);
+	}
+	if (nf_debug & (1 << NF_IP_LOCAL_OUT)) {
+		printk("LOCAL_OUT ");
+		nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+	}
+	if (nf_debug & (1 << NF_IP_POST_ROUTING)) {
+		printk("POST_ROUTING ");
+		nf_debug ^= (1 << NF_IP_POST_ROUTING);
+	}
+	if (nf_debug)
+		printk("Crap bits: 0x%04X", nf_debug);
+	printk("\n");
+}
+
+void nf_dump_skb(int pf, struct sk_buff *skb)
+{
+	printk("skb: pf=%i %s dev=%s len=%u\n", 
+	       pf,
+	       skb->sk ? "(owned)" : "(unowned)",
+	       skb->dev ? skb->dev->name : "(no dev)",
+	       skb->len);
+	switch (pf) {
+	case PF_INET: {
+		const struct iphdr *ip = skb->nh.iph;
+		__u32 *opt = (__u32 *) (ip + 1);
+		int opti;
+		__u16 src_port = 0, dst_port = 0;
+
+		if (ip->protocol == IPPROTO_TCP
+		    || ip->protocol == IPPROTO_UDP) {
+			struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+			src_port = ntohs(tcp->source);
+			dst_port = ntohs(tcp->dest);
+		}
+	
+		printk("PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu"
+		       " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
+		       ip->protocol, NIPQUAD(ip->saddr),
+		       src_port, NIPQUAD(ip->daddr),
+		       dst_port,
+		       ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+		       ntohs(ip->frag_off), ip->ttl);
+
+		for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+			printk(" O=0x%8.8X", *opt++);
+		printk("\n");
+	}
+	}
+}
+
+void nf_debug_ip_local_deliver(struct sk_buff *skb)
+{
+	/* If it's a loopback packet, it must have come through
+	 * NF_IP_LOCAL_OUT, NF_IP_RAW_INPUT, NF_IP_PRE_ROUTING and
+	 * NF_IP_LOCAL_IN.  Otherwise, must have gone through
+	 * NF_IP_RAW_INPUT and NF_IP_PRE_ROUTING.  */
+	if (!skb->dev) {
+		printk("ip_local_deliver: skb->dev is NULL.\n");
+	}
+	else if (strcmp(skb->dev->name, "lo") == 0) {
+		if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				      | (1 << NF_IP_POST_ROUTING)
+				      | (1 << NF_IP_PRE_ROUTING)
+				      | (1 << NF_IP_LOCAL_IN))) {
+			printk("ip_local_deliver: bad loopback skb: ");
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	}
+	else {
+		if (skb->nf_debug != ((1<<NF_IP_PRE_ROUTING)
+				      | (1<<NF_IP_LOCAL_IN))) {
+			printk("ip_local_deliver: bad non-lo skb: ");
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	}
+}
+
+void nf_debug_ip_loopback_xmit(struct sk_buff *newskb)
+{
+	if (newskb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				 | (1 << NF_IP_POST_ROUTING))) {
+		printk("ip_dev_loopback_xmit: bad owned skb = %p: ", 
+		       newskb);
+		debug_print_hooks_ip(newskb->nf_debug);
+		nf_dump_skb(PF_INET, newskb);
+	}
+	/* Clear to avoid confusing input check */
+	newskb->nf_debug = 0;
+}
+
+void nf_debug_ip_finish_output2(struct sk_buff *skb)
+{
+	/* If it's owned, it must have gone through the
+	 * NF_IP_LOCAL_OUT and NF_IP_POST_ROUTING.
+	 * Otherwise, must have gone through
+	 * NF_IP_PRE_ROUTING, NF_IP_FORWARD and NF_IP_POST_ROUTING.
+	 */
+	if (skb->sk) {
+		if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				      | (1 << NF_IP_POST_ROUTING))) {
+			printk("ip_finish_output: bad owned skb = %p: ", skb);
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	} else {
+		if (skb->nf_debug != ((1 << NF_IP_PRE_ROUTING)
+				      | (1 << NF_IP_FORWARD)
+				      | (1 << NF_IP_POST_ROUTING))) {
+			/* Fragments, entunnelled packets, TCP RSTs
+                           generated by ipt_REJECT will have no
+                           owners, but still may be local */
+			if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+					      | (1 << NF_IP_POST_ROUTING))){
+				printk("ip_finish_output:"
+				       " bad unowned skb = %p: ",skb);
+				debug_print_hooks_ip(skb->nf_debug);
+				nf_dump_skb(PF_INET, skb);
+			}
+		}
+	}
+}
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+/* Call get/setsockopt() */
+static int nf_sockopt(struct sock *sk, int pf, int val, 
+		      char *opt, int *len, int get)
+{
+	struct list_head *i;
+	struct nf_sockopt_ops *ops;
+	int ret;
+
+	if (down_interruptible(&nf_sockopt_mutex) != 0)
+		return -EINTR;
+
+	for (i = nf_sockopts.next; i != &nf_sockopts; i = i->next) {
+		ops = (struct nf_sockopt_ops *)i;
+		if (ops->pf == pf) {
+			if (get) {
+				if (val >= ops->get_optmin
+				    && val < ops->get_optmax) {
+					ops->use++;
+					up(&nf_sockopt_mutex);
+					ret = ops->get(sk, val, opt, len);
+					goto out;
+				}
+			} else {
+				if (val >= ops->set_optmin
+				    && val < ops->set_optmax) {
+					ops->use++;
+					up(&nf_sockopt_mutex);
+					ret = ops->set(sk, val, opt, *len);
+					goto out;
+				}
+			}
+		}
+	}
+	up(&nf_sockopt_mutex);
+	return -ENOPROTOOPT;
+	
+ out:
+	down(&nf_sockopt_mutex);
+	ops->use--;
+	if (ops->cleanup_task)
+		wake_up_process(ops->cleanup_task);
+	up(&nf_sockopt_mutex);
+	return ret;
+}
+
+int nf_setsockopt(struct sock *sk, int pf, int val, char *opt,
+		  int len)
+{
+	return nf_sockopt(sk, pf, val, opt, &len, 0);
+}
+
+int nf_getsockopt(struct sock *sk, int pf, int val, char *opt, int *len)
+{
+	return nf_sockopt(sk, pf, val, opt, len, 1);
+}
+
+static unsigned int nf_iterate(struct list_head *head,
+			       struct sk_buff **skb,
+			       int hook,
+			       const struct net_device *indev,
+			       const struct net_device *outdev,
+			       struct list_head **i,
+			       int (*okfn)(struct sk_buff *),
+			       int hook_thresh)
+{
+	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
+
+		if (hook_thresh > elem->priority)
+			continue;
+
+		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+		case NF_QUEUE:
+			return NF_QUEUE;
+
+		case NF_STOLEN:
+			return NF_STOLEN;
+
+		case NF_DROP:
+			return NF_DROP;
+
+		case NF_REPEAT:
+			*i = (*i)->prev;
+			break;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+		case NF_ACCEPT:
+			break;
+
+		default:
+			NFDEBUG("Evil return from %p(%u).\n", 
+				elem->hook, hook);
+#endif
+		}
+	}
+	return NF_ACCEPT;
+}
+
+int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
+{      
+	int ret;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	if (queue_handler[pf].outfn)
+		ret = -EBUSY;
+	else {
+		queue_handler[pf].outfn = outfn;
+		queue_handler[pf].data = data;
+		ret = 0;
+	}
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+	return ret;
+}
+
+/* The caller must flush their queue before this */
+int nf_unregister_queue_handler(int pf)
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	queue_handler[pf].outfn = NULL;
+	queue_handler[pf].data = NULL;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	return 0;
+}
+
+/* 
+ * Any packet that leaves via this function must come back 
+ * through nf_reinject().
+ */
+static void nf_queue(struct sk_buff *skb, 
+		     struct list_head *elem, 
+		     int pf, unsigned int hook,
+		     struct net_device *indev,
+		     struct net_device *outdev,
+		     int (*okfn)(struct sk_buff *))
+{
+	int status;
+	struct nf_info *info;
+	struct net_device *physindev;
+	struct net_device *physoutdev;
+
+	if (!queue_handler[pf].outfn) {
+		kfree_skb(skb);
+		return;
+	}
+
+	info = kmalloc(sizeof(*info), GFP_ATOMIC);
+	if (!info) {
+		if (net_ratelimit())
+			printk(KERN_ERR "OOM queueing packet %p\n",
+			       skb);
+		kfree_skb(skb);
+		return;
+	}
+
+	*info = (struct nf_info) { 
+		(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
+
+	/* Bump dev refs so they don't vanish while packet is out */
+	if (indev) dev_hold(indev);
+	if (outdev) dev_hold(outdev);
+
+	if ((physindev = skb->physindev)) dev_hold(physindev);
+	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
+
+	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+	if (status < 0) {
+		/* James M doesn't say fuck enough. */
+		if (indev) dev_put(indev);
+		if (outdev) dev_put(outdev);
+		if (physindev) dev_put(physindev);
+		if (physoutdev) dev_put(physoutdev);
+		kfree(info);
+		kfree_skb(skb);
+		return;
+	}
+}
+
+int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+		 struct net_device *indev,
+		 struct net_device *outdev,
+		 int (*okfn)(struct sk_buff *),
+		 int hook_thresh)
+{
+	struct list_head *elem;
+	unsigned int verdict;
+	int ret = 0;
+
+	/* This stopgap cannot be removed until all the hooks are audited. */
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	if (skb->ip_summed == CHECKSUM_HW) {
+		if (outdev == NULL) {
+			skb->ip_summed = CHECKSUM_NONE;
+		} else {
+			skb_checksum_help(skb);
+		}
+	}
+
+	/* We may already have this, but read-locks nest anyway */
+	br_read_lock_bh(BR_NETPROTO_LOCK);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	if (skb->nf_debug & (1 << hook)) {
+		printk("nf_hook: hook %i already set.\n", hook);
+		nf_dump_skb(pf, skb);
+	}
+	skb->nf_debug |= (1 << hook);
+#endif
+
+	elem = &nf_hooks[pf][hook];
+	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+			     outdev, &elem, okfn, hook_thresh);
+	if (verdict == NF_QUEUE) {
+		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+	}
+
+	switch (verdict) {
+	case NF_ACCEPT:
+		ret = okfn(skb);
+		break;
+
+	case NF_DROP:
+		kfree_skb(skb);
+		ret = -EPERM;
+		break;
+	}
+
+	br_read_unlock_bh(BR_NETPROTO_LOCK);
+	return ret;
+}
+
+void nf_reinject(struct sk_buff *skb, struct nf_info *info,
+		 unsigned int verdict)
+{
+	struct list_head *elem = &info->elem->list;
+	struct list_head *i;
+
+	/* We don't have BR_NETPROTO_LOCK here */
+	br_read_lock_bh(BR_NETPROTO_LOCK);
+	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+		if (i == &nf_hooks[info->pf][info->hook]) {
+			/* The module which sent it to userspace is gone. */
+			NFDEBUG("%s: module disappeared, dropping packet.\n",
+			         __FUNCTION__);
+			verdict = NF_DROP;
+			break;
+		}
+	}
+
+	/* Continue traversal iff userspace said ok... */
+	if (verdict == NF_REPEAT) {
+		elem = elem->prev;
+		verdict = NF_ACCEPT;
+	}
+
+	if (verdict == NF_ACCEPT) {
+		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+				     &skb, info->hook, 
+				     info->indev, info->outdev, &elem,
+				     info->okfn, INT_MIN);
+	}
+
+	switch (verdict) {
+	case NF_ACCEPT:
+		info->okfn(skb);
+		break;
+
+	case NF_QUEUE:
+		nf_queue(skb, elem, info->pf, info->hook, 
+			 info->indev, info->outdev, info->okfn);
+		break;
+
+	case NF_DROP:
+		kfree_skb(skb);
+		break;
+	}
+	br_read_unlock_bh(BR_NETPROTO_LOCK);
+
+	/* Release those devices we held, or Alexey will kill me. */
+	if (info->indev) dev_put(info->indev);
+	if (info->outdev) dev_put(info->outdev);
+	
+	kfree(info);
+	return;
+}
+
+#ifdef CONFIG_INET
+/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
+int ip_route_me_harder(struct sk_buff **pskb)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	struct rtable *rt;
+	struct rt_key key = { dst:iph->daddr,
+			      src:iph->saddr,
+			      oif:(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			      tos:RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+			      fwmark:(*pskb)->nfmark
+#endif
+			    };
+	struct net_device *dev_src = NULL;
+	int err;
+
+	/* accomodate ip_route_output_slow(), which expects the key src to be
+	   0 or a local address; however some non-standard hacks like
+	   ipt_REJECT.c:send_reset() can cause packets with foreign
+           saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */
+	if(key.src && !(dev_src = ip_dev_find(key.src)))
+		key.src = 0;
+
+	if ((err=ip_route_output_key(&rt, &key)) != 0) {
+		printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n",
+			NIPQUAD(iph->daddr), NIPQUAD(iph->saddr),
+			(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+			(*pskb)->nfmark,
+#else
+			0UL,
+#endif
+			err);
+		goto out;
+	}
+
+	/* Drop old route. */
+	dst_release((*pskb)->dst);
+
+	(*pskb)->dst = &rt->u.dst;
+
+	/* Change in oif may mean change in hh_len. */
+	if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) {
+		struct sk_buff *nskb;
+
+		nskb = skb_realloc_headroom(*pskb,
+					    (*pskb)->dst->dev->hard_header_len);
+		if (!nskb) {
+			err = -ENOMEM;
+			goto out;
+		}
+		if ((*pskb)->sk)
+			skb_set_owner_w(nskb, (*pskb)->sk);
+		kfree_skb(*pskb);
+		*pskb = nskb;
+	}
+
+out:
+	if (dev_src)
+		dev_put(dev_src);
+
+	return err;
+}
+#endif /*CONFIG_INET*/
+
+/* This does not belong here, but ipt_REJECT needs it if connection
+   tracking in use: without this, connection may not be in hash table,
+   and hence manufactured ICMP or RST packets will not be associated
+   with it. */
+void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
+
+void __init netfilter_init(void)
+{
+	int i, h;
+
+	for (i = 0; i < NPROTO; i++) {
+		for (h = 0; h < NF_MAX_HOOKS; h++)
+			INIT_LIST_HEAD(&nf_hooks[i][h]);
+	}
+}
diff --git a/br-nf-bds/linux/net/core/skbuff.c b/br-nf-bds/linux/net/core/skbuff.c
new file mode 100644
index 0000000..d77feab
--- /dev/null
+++ b/br-nf-bds/linux/net/core/skbuff.c
@@ -0,0 +1,1211 @@
+/*
+ *	Routines having to do with the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
+ *			Florian La Roche <rzsfl@rz.uni-sb.de>
+ *
+ *	Version:	$Id: skbuff.c,v 1.4 2002/09/17 21:49:57 bdschuym Exp $
+ *
+ *	Fixes:	
+ *		Alan Cox	:	Fixed the worst of the load balancer bugs.
+ *		Dave Platt	:	Interrupt stacking fix.
+ *	Richard Kooijman	:	Timestamp fixes.
+ *		Alan Cox	:	Changed buffer format.
+ *		Alan Cox	:	destructor hook for AF_UNIX etc.
+ *		Linus Torvalds	:	Better skb_clone.
+ *		Alan Cox	:	Added skb_copy.
+ *		Alan Cox	:	Added all the changed routines Linus
+ *					only put in the headers
+ *		Ray VanTassle	:	Fixed --skb->lock in free
+ *		Alan Cox	:	skb_copy copy arp field
+ *		Andi Kleen	:	slabified it.
+ *
+ *	NOTE:
+ *		The __skb_ routines should be called with interrupts 
+ *	disabled, or you better be *real* sure that the operation is atomic 
+ *	with respect to whatever list is being frobbed (e.g. via lock_sock()
+ *	or via disabling bottom half handlers, etc).
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+/*
+ *	The functions in this file will not compile correctly with gcc 2.4.x
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/cache.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+#include <net/protocol.h>
+#include <net/dst.h>
+#include <net/sock.h>
+#include <net/checksum.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+int sysctl_hot_list_len = 128;
+
+static kmem_cache_t *skbuff_head_cache;
+
+static union {
+	struct sk_buff_head	list;
+	char			pad[SMP_CACHE_BYTES];
+} skb_head_pool[NR_CPUS];
+
+/*
+ *	Keep out-of-line to prevent kernel bloat.
+ *	__builtin_return_address is not used because it is not always
+ *	reliable. 
+ */
+
+/**
+ *	skb_over_panic	- 	private function
+ *	@skb: buffer
+ *	@sz: size
+ *	@here: address
+ *
+ *	Out of line support code for skb_put(). Not user callable.
+ */
+ 
+void skb_over_panic(struct sk_buff *skb, int sz, void *here)
+{
+	printk("skput:over: %p:%d put:%d dev:%s", 
+		here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+	BUG();
+}
+
+/**
+ *	skb_under_panic	- 	private function
+ *	@skb: buffer
+ *	@sz: size
+ *	@here: address
+ *
+ *	Out of line support code for skb_push(). Not user callable.
+ */
+ 
+
+void skb_under_panic(struct sk_buff *skb, int sz, void *here)
+{
+        printk("skput:under: %p:%d put:%d dev:%s",
+                here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+	BUG();
+}
+
+static __inline__ struct sk_buff *skb_head_from_pool(void)
+{
+	struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list;
+
+	if (skb_queue_len(list)) {
+		struct sk_buff *skb;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		skb = __skb_dequeue(list);
+		local_irq_restore(flags);
+		return skb;
+	}
+	return NULL;
+}
+
+static __inline__ void skb_head_to_pool(struct sk_buff *skb)
+{
+	struct sk_buff_head *list = &skb_head_pool[smp_processor_id()].list;
+
+	if (skb_queue_len(list) < sysctl_hot_list_len) {
+		unsigned long flags;
+
+		local_irq_save(flags);
+		__skb_queue_head(list, skb);
+		local_irq_restore(flags);
+
+		return;
+	}
+	kmem_cache_free(skbuff_head_cache, skb);
+}
+
+
+/* 	Allocate a new skbuff. We do this ourselves so we can fill in a few
+ *	'private' fields and also do memory statistics to find all the
+ *	[BEEP] leaks.
+ * 
+ */
+
+/**
+ *	alloc_skb	-	allocate a network buffer
+ *	@size: size to allocate
+ *	@gfp_mask: allocation mask
+ *
+ *	Allocate a new &sk_buff. The returned buffer has no headroom and a
+ *	tail room of size bytes. The object has a reference count of one.
+ *	The return is the buffer. On a failure the return is %NULL.
+ *
+ *	Buffers may only be allocated from interrupts using a @gfp_mask of
+ *	%GFP_ATOMIC.
+ */
+ 
+struct sk_buff *alloc_skb(unsigned int size,int gfp_mask)
+{
+	struct sk_buff *skb;
+	u8 *data;
+
+	if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
+		static int count = 0;
+		if (++count < 5) {
+			printk(KERN_ERR "alloc_skb called nonatomically "
+			       "from interrupt %p\n", NET_CALLER(size));
+ 			BUG();
+		}
+		gfp_mask &= ~__GFP_WAIT;
+	}
+
+	/* Get the HEAD */
+	skb = skb_head_from_pool();
+	if (skb == NULL) {
+		skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & ~__GFP_DMA);
+		if (skb == NULL)
+			goto nohead;
+	}
+
+	/* Get the DATA. Size must match skb_add_mtu(). */
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (data == NULL)
+		goto nodata;
+
+	/* XXX: does not include slab overhead */ 
+	skb->truesize = size + sizeof(struct sk_buff);
+
+	/* Load the data pointers. */
+	skb->head = data;
+	skb->data = data;
+	skb->tail = data;
+	skb->end = data + size;
+
+	/* Set up other state */
+	skb->len = 0;
+	skb->cloned = 0;
+	skb->data_len = 0;
+
+	atomic_set(&skb->users, 1); 
+	atomic_set(&(skb_shinfo(skb)->dataref), 1);
+	skb_shinfo(skb)->nr_frags = 0;
+	skb_shinfo(skb)->frag_list = NULL;
+	return skb;
+
+nodata:
+	skb_head_to_pool(skb);
+nohead:
+	return NULL;
+}
+
+
+/*
+ *	Slab constructor for a skb head. 
+ */ 
+static inline void skb_headerinit(void *p, kmem_cache_t *cache, 
+				  unsigned long flags)
+{
+	struct sk_buff *skb = p;
+
+	skb->next = NULL;
+	skb->prev = NULL;
+	skb->list = NULL;
+	skb->sk = NULL;
+	skb->stamp.tv_sec=0;	/* No idea about time */
+	skb->dev = NULL;
+	skb->physindev = NULL;
+	skb->physoutdev = NULL;
+	skb->dst = NULL;
+	memset(skb->cb, 0, sizeof(skb->cb));
+	skb->pkt_type = PACKET_HOST;	/* Default type */
+	skb->ip_summed = 0;
+	skb->priority = 0;
+	skb->security = 0;	/* By default packets are insecure */
+	skb->destructor = NULL;
+
+#ifdef CONFIG_NETFILTER
+	skb->nfmark = skb->nfcache = 0;
+	skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+#endif
+#ifdef CONFIG_NET_SCHED
+	skb->tc_index = 0;
+#endif
+}
+
+static void skb_drop_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+	skb_shinfo(skb)->frag_list = NULL;
+
+	do {
+		struct sk_buff *this = list;
+		list = list->next;
+		kfree_skb(this);
+	} while (list);
+}
+
+static void skb_clone_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list;
+
+	for (list = skb_shinfo(skb)->frag_list; list; list=list->next)
+		skb_get(list);
+}
+
+static void skb_release_data(struct sk_buff *skb)
+{
+	if (!skb->cloned ||
+	    atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) {
+		if (skb_shinfo(skb)->nr_frags) {
+			int i;
+			for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+				put_page(skb_shinfo(skb)->frags[i].page);
+		}
+
+		if (skb_shinfo(skb)->frag_list)
+			skb_drop_fraglist(skb);
+
+		kfree(skb->head);
+	}
+}
+
+/*
+ *	Free an skbuff by memory without cleaning the state. 
+ */
+void kfree_skbmem(struct sk_buff *skb)
+{
+	skb_release_data(skb);
+	skb_head_to_pool(skb);
+}
+
+/**
+ *	__kfree_skb - private function 
+ *	@skb: buffer
+ *
+ *	Free an sk_buff. Release anything attached to the buffer. 
+ *	Clean the state. This is an internal helper function. Users should
+ *	always call kfree_skb
+ */
+
+void __kfree_skb(struct sk_buff *skb)
+{
+	if (skb->list) {
+	 	printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
+		       "on a list (from %p).\n", NET_CALLER(skb));
+		BUG();
+	}
+
+	dst_release(skb->dst);
+	if(skb->destructor) {
+		if (in_irq()) {
+			printk(KERN_WARNING "Warning: kfree_skb on hard IRQ %p\n",
+				NET_CALLER(skb));
+		}
+		skb->destructor(skb);
+	}
+#ifdef CONFIG_NETFILTER
+	nf_conntrack_put(skb->nfct);
+#endif
+	skb_headerinit(skb, NULL, 0);  /* clean state */
+	kfree_skbmem(skb);
+}
+
+/**
+ *	skb_clone	-	duplicate an sk_buff
+ *	@skb: buffer to clone
+ *	@gfp_mask: allocation priority
+ *
+ *	Duplicate an &sk_buff. The new one is not owned by a socket. Both
+ *	copies share the same packet data but not structure. The new
+ *	buffer has a reference count of 1. If the allocation fails the 
+ *	function returns %NULL otherwise the new buffer is returned.
+ *	
+ *	If this function is called from an interrupt gfp_mask() must be
+ *	%GFP_ATOMIC.
+ */
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
+{
+	struct sk_buff *n;
+
+	n = skb_head_from_pool();
+	if (!n) {
+		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+		if (!n)
+			return NULL;
+	}
+
+#define C(x) n->x = skb->x
+
+	n->next = n->prev = NULL;
+	n->list = NULL;
+	n->sk = NULL;
+	C(stamp);
+	C(dev);
+	C(physindev);
+	C(physoutdev);
+	C(h);
+	C(nh);
+	C(mac);
+	C(dst);
+	dst_clone(n->dst);
+	memcpy(n->cb, skb->cb, sizeof(skb->cb));
+	C(len);
+	C(data_len);
+	C(csum);
+	n->cloned = 1;
+	C(pkt_type);
+	C(ip_summed);
+	C(priority);
+	atomic_set(&n->users, 1);
+	C(protocol);
+	C(security);
+	C(truesize);
+	C(head);
+	C(data);
+	C(tail);
+	C(end);
+	n->destructor = NULL;
+#ifdef CONFIG_NETFILTER
+	C(nfmark);
+	C(nfcache);
+	C(nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+	C(nf_debug);
+#endif
+#endif /*CONFIG_NETFILTER*/
+#if defined(CONFIG_HIPPI)
+	C(private);
+#endif
+#ifdef CONFIG_NET_SCHED
+	C(tc_index);
+#endif
+
+	atomic_inc(&(skb_shinfo(skb)->dataref));
+	skb->cloned = 1;
+#ifdef CONFIG_NETFILTER
+	nf_conntrack_get(skb->nfct);
+#endif
+	return n;
+}
+
+static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	unsigned long offset = new->data - old->data;
+
+	new->list=NULL;
+	new->sk=NULL;
+	new->dev=old->dev;
+	new->physindev=old->physindev;
+	new->physoutdev=old->physoutdev;
+	new->priority=old->priority;
+	new->protocol=old->protocol;
+	new->dst=dst_clone(old->dst);
+	new->h.raw=old->h.raw+offset;
+	new->nh.raw=old->nh.raw+offset;
+	new->mac.raw=old->mac.raw+offset;
+	memcpy(new->cb, old->cb, sizeof(old->cb));
+	atomic_set(&new->users, 1);
+	new->pkt_type=old->pkt_type;
+	new->stamp=old->stamp;
+	new->destructor = NULL;
+	new->security=old->security;
+#ifdef CONFIG_NETFILTER
+	new->nfmark=old->nfmark;
+	new->nfcache=old->nfcache;
+	new->nfct=old->nfct;
+	nf_conntrack_get(new->nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+	new->nf_debug=old->nf_debug;
+#endif
+#endif
+#ifdef CONFIG_NET_SCHED
+	new->tc_index = old->tc_index;
+#endif
+}
+
+/**
+ *	skb_copy	-	create private copy of an sk_buff
+ *	@skb: buffer to copy
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and its data. This is used when the
+ *	caller wishes to modify the data and needs a private copy of the 
+ *	data to alter. Returns %NULL on failure or the pointer to the buffer
+ *	on success. The returned buffer has a reference count of 1.
+ *
+ *	As by-product this function converts non-linear &sk_buff to linear
+ *	one, so that &sk_buff becomes completely private and caller is allowed
+ *	to modify all the data of returned buffer. This means that this
+ *	function is not recommended for use in circumstances when only
+ *	header is going to be modified. Use pskb_copy() instead.
+ */
+ 
+struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
+{
+	struct sk_buff *n;
+	int headerlen = skb->data-skb->head;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+	n=alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
+	if(n==NULL)
+		return NULL;
+
+	/* Set the data pointer */
+	skb_reserve(n,headerlen);
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+	n->csum = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	if (skb_copy_bits(skb, -headerlen, n->head, headerlen+skb->len))
+		BUG();
+
+	copy_skb_header(n, skb);
+
+	return n;
+}
+
+/* Keep head the same: replace data */
+int skb_linearize(struct sk_buff *skb, int gfp_mask)
+{
+	unsigned int size;
+	u8 *data;
+	long offset;
+	int headerlen = skb->data - skb->head;
+	int expand = (skb->tail+skb->data_len) - skb->end;
+
+	if (skb_shared(skb))
+		BUG();
+
+	if (expand <= 0)
+		expand = 0;
+
+	size = (skb->end - skb->head + expand);
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (data == NULL)
+		return -ENOMEM;
+
+	/* Copy entire thing */
+	if (skb_copy_bits(skb, -headerlen, data, headerlen+skb->len))
+		BUG();
+
+	/* Offset between the two in bytes */
+	offset = data - skb->head;
+
+	/* Free old data. */
+	skb_release_data(skb);
+
+	skb->head = data;
+	skb->end  = data + size;
+
+	/* Set up new pointers */
+	skb->h.raw += offset;
+	skb->nh.raw += offset;
+	skb->mac.raw += offset;
+	skb->tail += offset;
+	skb->data += offset;
+
+	/* Set up shinfo */
+	atomic_set(&(skb_shinfo(skb)->dataref), 1);
+	skb_shinfo(skb)->nr_frags = 0;
+	skb_shinfo(skb)->frag_list = NULL;
+
+	/* We are no longer a clone, even if we were. */
+	skb->cloned = 0;
+
+	skb->tail += skb->data_len;
+	skb->data_len = 0;
+	return 0;
+}
+
+
+/**
+ *	pskb_copy	-	create copy of an sk_buff with private head.
+ *	@skb: buffer to copy
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and part of its data, located
+ *	in header. Fragmented data remain shared. This is used when
+ *	the caller wishes to modify only header of &sk_buff and needs
+ *	private copy of the header to alter. Returns %NULL on failure
+ *	or the pointer to the buffer on success.
+ *	The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
+{
+	struct sk_buff *n;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+	n=alloc_skb(skb->end - skb->head, gfp_mask);
+	if(n==NULL)
+		return NULL;
+
+	/* Set the data pointer */
+	skb_reserve(n,skb->data-skb->head);
+	/* Set the tail pointer and length */
+	skb_put(n,skb_headlen(skb));
+	/* Copy the bytes */
+	memcpy(n->data, skb->data, n->len);
+	n->csum = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	n->data_len = skb->data_len;
+	n->len = skb->len;
+
+	if (skb_shinfo(skb)->nr_frags) {
+		int i;
+
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+			get_page(skb_shinfo(n)->frags[i].page);
+		}
+		skb_shinfo(n)->nr_frags = i;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
+		skb_clone_fraglist(n);
+	}
+
+	copy_skb_header(n, skb);
+
+	return n;
+}
+
+/**
+ *	pskb_expand_head - reallocate header of &sk_buff
+ *	@skb: buffer to reallocate
+ *	@nhead: room to add at head
+ *	@ntail: room to add at tail
+ *	@gfp_mask: allocation priority
+ *
+ *	Expands (or creates identical copy, if &nhead and &ntail are zero)
+ *	header of skb. &sk_buff itself is not changed. &sk_buff MUST have
+ *	reference count of 1. Returns zero in the case of success or error,
+ *	if expansion failed. In the last case, &sk_buff is not changed.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask)
+{
+	int i;
+	u8 *data;
+	int size = nhead + (skb->end - skb->head) + ntail;
+	long off;
+
+	if (skb_shared(skb))
+		BUG();
+
+	size = SKB_DATA_ALIGN(size);
+
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (data == NULL)
+		goto nodata;
+
+	/* Copy only real data... and, alas, header. This should be
+	 * optimized for the cases when header is void. */
+	memcpy(data+nhead, skb->head, skb->tail-skb->head);
+	memcpy(data+size, skb->end, sizeof(struct skb_shared_info));
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++)
+		get_page(skb_shinfo(skb)->frags[i].page);
+
+	if (skb_shinfo(skb)->frag_list)
+		skb_clone_fraglist(skb);
+
+	skb_release_data(skb);
+
+	off = (data+nhead) - skb->head;
+
+	skb->head = data;
+	skb->end  = data+size;
+
+	skb->data += off;
+	skb->tail += off;
+	skb->mac.raw += off;
+	skb->h.raw += off;
+	skb->nh.raw += off;
+	skb->cloned = 0;
+	atomic_set(&skb_shinfo(skb)->dataref, 1);
+	return 0;
+
+nodata:
+	return -ENOMEM;
+}
+
+/* Make private copy of skb with writable head and some headroom */
+
+struct sk_buff *
+skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
+{
+	struct sk_buff *skb2;
+	int delta = headroom - skb_headroom(skb);
+
+	if (delta <= 0)
+		return pskb_copy(skb, GFP_ATOMIC);
+
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+	if (skb2 == NULL ||
+	    !pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC))
+		return skb2;
+
+	kfree_skb(skb2);
+	return NULL;
+}
+
+
+/**
+ *	skb_copy_expand	-	copy and expand sk_buff
+ *	@skb: buffer to copy
+ *	@newheadroom: new free bytes at head
+ *	@newtailroom: new free bytes at tail
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and its data and while doing so 
+ *	allocate additional space.
+ *
+ *	This is used when the caller wishes to modify the data and needs a 
+ *	private copy of the data to alter as well as more space for new fields.
+ *	Returns %NULL on failure or the pointer to the buffer
+ *	on success. The returned buffer has a reference count of 1.
+ *
+ *	You must pass %GFP_ATOMIC as the allocation priority if this function
+ *	is called from an interrupt.
+ */
+ 
+
+struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
+				int newheadroom,
+				int newtailroom,
+				int gfp_mask)
+{
+	struct sk_buff *n;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+ 	 
+	n=alloc_skb(newheadroom + skb->len + newtailroom,
+		    gfp_mask);
+	if(n==NULL)
+		return NULL;
+
+	skb_reserve(n,newheadroom);
+
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+
+	/* Copy the data only. */
+	if (skb_copy_bits(skb, 0, n->data, skb->len))
+		BUG();
+
+	copy_skb_header(n, skb);
+	return n;
+}
+
+/* Trims skb to length len. It can change skb pointers, if "realloc" is 1.
+ * If realloc==0 and trimming is impossible without change of data,
+ * it is BUG().
+ */
+
+int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc)
+{
+	int offset = skb_headlen(skb);
+	int nfrags = skb_shinfo(skb)->nr_frags;
+	int i;
+
+	for (i=0; i<nfrags; i++) {
+		int end = offset + skb_shinfo(skb)->frags[i].size;
+		if (end > len) {
+			if (skb_cloned(skb)) {
+				if (!realloc)
+					BUG();
+				if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+					return -ENOMEM;
+			}
+			if (len <= offset) {
+				put_page(skb_shinfo(skb)->frags[i].page);
+				skb_shinfo(skb)->nr_frags--;
+			} else {
+				skb_shinfo(skb)->frags[i].size = len-offset;
+			}
+		}
+		offset = end;
+	}
+
+	if (offset < len) {
+		skb->data_len -= skb->len - len;
+		skb->len = len;
+	} else {
+		if (len <= skb_headlen(skb)) {
+			skb->len = len;
+			skb->data_len = 0;
+			skb->tail = skb->data + len;
+			if (skb_shinfo(skb)->frag_list && !skb_cloned(skb))
+				skb_drop_fraglist(skb);
+		} else {
+			skb->data_len -= skb->len - len;
+			skb->len = len;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *	__pskb_pull_tail - advance tail of skb header 
+ *	@skb: buffer to reallocate
+ *	@delta: number of bytes to advance tail
+ *
+ *	The function makes a sense only on a fragmented &sk_buff,
+ *	it expands header moving its tail forward and copying necessary
+ *	data from fragmented part.
+ *
+ *	&sk_buff MUST have reference count of 1.
+ *
+ *	Returns %NULL (and &sk_buff does not change) if pull failed
+ *	or value of new tail of skb in the case of success.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+/* Moves tail of skb head forward, copying data from fragmented part,
+ * when it is necessary.
+ * 1. It may fail due to malloc failure.
+ * 2. It may change skb pointers.
+ *
+ * It is pretty complicated. Luckily, it is called only in exceptional cases.
+ */
+unsigned char * __pskb_pull_tail(struct sk_buff *skb, int delta)
+{
+	int i, k, eat;
+
+	/* If skb has not enough free space at tail, get new one
+	 * plus 128 bytes for future expansions. If we have enough
+	 * room at tail, reallocate without expansion only if skb is cloned.
+	 */
+	eat = (skb->tail+delta) - skb->end;
+
+	if (eat > 0 || skb_cloned(skb)) {
+		if (pskb_expand_head(skb, 0, eat>0 ? eat+128 : 0, GFP_ATOMIC))
+			return NULL;
+	}
+
+	if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
+		BUG();
+
+	/* Optimization: no fragments, no reasons to preestimate
+	 * size of pulled pages. Superb.
+	 */
+	if (skb_shinfo(skb)->frag_list == NULL)
+		goto pull_pages;
+
+	/* Estimate size of pulled pages. */
+	eat = delta;
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size >= eat)
+			goto pull_pages;
+		eat -= skb_shinfo(skb)->frags[i].size;
+	}
+
+	/* If we need update frag list, we are in troubles.
+	 * Certainly, it possible to add an offset to skb data,
+	 * but taking into account that pulling is expected to
+	 * be very rare operation, it is worth to fight against
+	 * further bloating skb head and crucify ourselves here instead.
+	 * Pure masohism, indeed. 8)8)
+	 */
+	if (eat) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+		struct sk_buff *clone = NULL;
+		struct sk_buff *insp = NULL;
+
+		do {
+			if (list == NULL)
+				BUG();
+
+			if (list->len <= eat) {
+				/* Eaten as whole. */
+				eat -= list->len;
+				list = list->next;
+				insp = list;
+			} else {
+				/* Eaten partially. */
+
+				if (skb_shared(list)) {
+					/* Sucks! We need to fork list. :-( */
+					clone = skb_clone(list, GFP_ATOMIC);
+					if (clone == NULL)
+						return NULL;
+					insp = list->next;
+					list = clone;
+				} else {
+					/* This may be pulled without
+					 * problems. */
+					insp = list;
+				}
+				if (pskb_pull(list, eat) == NULL) {
+					if (clone)
+						kfree_skb(clone);
+					return NULL;
+				}
+				break;
+			}
+		} while (eat);
+
+		/* Free pulled out fragments. */
+		while ((list = skb_shinfo(skb)->frag_list) != insp) {
+			skb_shinfo(skb)->frag_list = list->next;
+			kfree_skb(list);
+		}
+		/* And insert new clone at head. */
+		if (clone) {
+			clone->next = list;
+			skb_shinfo(skb)->frag_list = clone;
+		}
+	}
+	/* Success! Now we may commit changes to skb data. */
+
+pull_pages:
+	eat = delta;
+	k = 0;
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size <= eat) {
+			put_page(skb_shinfo(skb)->frags[i].page);
+			eat -= skb_shinfo(skb)->frags[i].size;
+		} else {
+			skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+			if (eat) {
+				skb_shinfo(skb)->frags[k].page_offset += eat;
+				skb_shinfo(skb)->frags[k].size -= eat;
+				eat = 0;
+			}
+			k++;
+		}
+	}
+	skb_shinfo(skb)->nr_frags = k;
+
+	skb->tail += delta;
+	skb->data_len -= delta;
+
+	return skb->tail;
+}
+
+/* Copy some data bits from skb to kernel buffer. */
+
+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+
+	if (offset > (int)skb->len-len)
+		goto fault;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		memcpy(to, skb->data + offset, copy);
+		if ((len -= copy) == 0)
+			return 0;
+		offset += copy;
+		to += copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			u8 *vaddr;
+
+			if (copy > len)
+				copy = len;
+
+			vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+			memcpy(to, vaddr+skb_shinfo(skb)->frags[i].page_offset+
+			       offset-start, copy);
+			kunmap_skb_frag(vaddr);
+
+			if ((len -= copy) == 0)
+				return 0;
+			offset += copy;
+			to += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				if (copy > len)
+					copy = len;
+				if (skb_copy_bits(list, offset-start, to, copy))
+					goto fault;
+				if ((len -= copy) == 0)
+					return 0;
+				offset += copy;
+				to += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return 0;
+
+fault:
+	return -EFAULT;
+}
+
+/* Checksum skb data. */
+
+unsigned int skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+	int pos = 0;
+
+	/* Checksum header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial(skb->data+offset, copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		pos = copy;
+	}
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial(vaddr + frag->page_offset +
+					     offset-start, copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			pos += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				unsigned int csum2;
+				if (copy > len)
+					copy = len;
+				csum2 = skb_checksum(list, offset-start, copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				pos += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return csum;
+
+	BUG();
+	return csum;
+}
+
+/* Both of above in one bottle. */
+
+unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+	int pos = 0;
+
+	/* Copy header. */
+	if ((copy = start-offset) > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial_copy_nocheck(skb->data+offset, to, copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		to += copy;
+		pos = copy;
+	}
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset+len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end-offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial_copy_nocheck(vaddr + frag->page_offset +
+						      offset-start, to, copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			to += copy;
+			pos += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list;
+
+		for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+			unsigned int csum2;
+			int end;
+
+			BUG_TRAP(start <= offset+len);
+
+			end = start + list->len;
+			if ((copy = end-offset) > 0) {
+				if (copy > len)
+					copy = len;
+				csum2 = skb_copy_and_csum_bits(list, offset-start, to, copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				to += copy;
+				pos += copy;
+			}
+			start = end;
+		}
+	}
+	if (len == 0)
+		return csum;
+
+	BUG();
+	return csum;
+}
+
+void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
+{
+	unsigned int csum;
+	long csstart;
+
+	if (skb->ip_summed == CHECKSUM_HW)
+		csstart = skb->h.raw - skb->data;
+	else
+		csstart = skb->len - skb->data_len;
+
+	if (csstart > skb->len - skb->data_len)
+		BUG();
+
+	memcpy(to, skb->data, csstart);
+
+	csum = 0;
+	if (csstart != skb->len)
+		csum = skb_copy_and_csum_bits(skb, csstart, to+csstart,
+				skb->len-csstart, 0);
+
+	if (skb->ip_summed == CHECKSUM_HW) {
+		long csstuff = csstart + skb->csum;
+
+		*((unsigned short *)(to + csstuff)) = csum_fold(csum);
+	}
+}
+
+#if 0
+/* 
+ * 	Tune the memory allocator for a new MTU size.
+ */
+void skb_add_mtu(int mtu)
+{
+	/* Must match allocation in alloc_skb */
+	mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info);
+
+	kmem_add_cache_size(mtu);
+}
+#endif
+
+void __init skb_init(void)
+{
+	int i;
+
+	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+					      sizeof(struct sk_buff),
+					      0,
+					      SLAB_HWCACHE_ALIGN,
+					      skb_headerinit, NULL);
+	if (!skbuff_head_cache)
+		panic("cannot create skbuff cache");
+
+	for (i=0; i<NR_CPUS; i++)
+		skb_queue_head_init(&skb_head_pool[i].list);
+}
diff --git a/br-nf-bds/linux/net/ipv4/ip_output.c b/br-nf-bds/linux/net/ipv4/ip_output.c
new file mode 100644
index 0000000..ad1f552
--- /dev/null
+++ b/br-nf-bds/linux/net/ipv4/ip_output.c
@@ -0,0 +1,1029 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The Internet Protocol (IP) output module.
+ *
+ * Version:	$Id: ip_output.c,v 1.4 2002/09/17 21:51:08 bdschuym Exp $
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <Alan.Cox@linux.org>
+ *		Richard Underwood
+ *		Stefan Becker, <stefanb@yello.ping.de>
+ *		Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *
+ *	See ip_input.c for original log
+ *
+ *	Fixes:
+ *		Alan Cox	:	Missing nonblock feature in ip_build_xmit.
+ *		Mike Kilburn	:	htons() missing in ip_build_xmit.
+ *		Bradford Johnson:	Fix faulty handling of some frames when 
+ *					no route is found.
+ *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
+ *					(in case if packet not accepted by
+ *					output firewall rules)
+ *		Mike McLagan	:	Routing by source
+ *		Alexey Kuznetsov:	use new route cache
+ *		Andi Kleen:		Fix broken PMTU recovery and remove
+ *					some redundant tests.
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
+ *		Andi Kleen	: 	Replace ip_reply with ip_send_reply.
+ *		Andi Kleen	:	Split fast and slow ip_build_xmit path 
+ *					for decreased register pressure on x86 
+ *					and more readibility. 
+ *		Marc Boucher	:	When call_out_firewall returns FW_QUEUE,
+ *					silently drop skb instead of failing with -EPERM.
+ *		Detlev Wengorz	:	Copy protocol for fragments.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <net/inetpeer.h>
+#include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/mroute.h>
+#include <linux/netlink.h>
+
+/*
+ *      Shall we try to damage output packets if routing dev changes?
+ */
+
+int sysctl_ip_dynaddr = 0;
+int sysctl_ip_default_ttl = IPDEFTTL;
+
+/* Generate a checksum for an outgoing IP datagram. */
+__inline__ void ip_send_check(struct iphdr *iph)
+{
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+}
+
+/* dev_loopback_xmit for use with netfilter. */
+static int ip_dev_loopback_xmit(struct sk_buff *newskb)
+{
+	newskb->mac.raw = newskb->data;
+	__skb_pull(newskb, newskb->nh.raw - newskb->data);
+	newskb->pkt_type = PACKET_LOOPBACK;
+	newskb->ip_summed = CHECKSUM_UNNECESSARY;
+	BUG_TRAP(newskb->dst);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	nf_debug_ip_loopback_xmit(newskb);
+#endif
+	netif_rx(newskb);
+	return 0;
+}
+
+/* Don't just hand NF_HOOK skb->dst->output, in case netfilter hook
+   changes route */
+static inline int
+output_maybe_reroute(struct sk_buff *skb)
+{
+	return skb->dst->output(skb);
+}
+
+/* 
+ *		Add an ip header to a skbuff and send it out.
+ */
+int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+			  u32 saddr, u32 daddr, struct ip_options *opt)
+{
+	struct rtable *rt = (struct rtable *)skb->dst;
+	struct iphdr *iph;
+
+	/* Build the IP header. */
+	if (opt)
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
+	else
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = sk->protinfo.af_inet.tos;
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		iph->frag_off = htons(IP_DF);
+	else
+		iph->frag_off = 0;
+	iph->ttl      = sk->protinfo.af_inet.ttl;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = sk->protocol;
+	iph->tot_len  = htons(skb->len);
+	ip_select_ident(iph, &rt->u.dst, sk);
+	skb->nh.iph   = iph;
+
+	if (opt && opt->optlen) {
+		iph->ihl += opt->optlen>>2;
+		ip_options_build(skb, opt, daddr, rt, 0);
+	}
+	ip_send_check(iph);
+
+	/* Send it out. */
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		       output_maybe_reroute);
+}
+
+static inline int ip_finish_output2(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct hh_cache *hh = dst->hh;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	nf_debug_ip_finish_output2(skb);
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+	if (hh) {
+		read_lock_bh(&hh->hh_lock);
+  		memcpy(skb->data - 16, hh->hh_data, 16);
+		read_unlock_bh(&hh->hh_lock);
+	        skb_push(skb, hh->hh_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	if (net_ratelimit())
+		printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+__inline__ int ip_finish_output(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dst->dev;
+
+	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_IP);
+
+	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
+		       ip_finish_output2);
+}
+
+int ip_mc_output(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct net_device *dev = rt->u.dst.dev;
+
+	/*
+	 *	If the indicated interface is up and running, send the packet.
+	 */
+	IP_INC_STATS(IpOutRequests);
+#ifdef CONFIG_IP_ROUTE_NAT
+	if (rt->rt_flags & RTCF_NAT)
+		ip_do_nat(skb);
+#endif
+
+	skb->dev = dev;
+	skb->protocol = __constant_htons(ETH_P_IP);
+
+	/*
+	 *	Multicasts are looped back for other local users
+	 */
+
+	if (rt->rt_flags&RTCF_MULTICAST) {
+		if ((!sk || sk->protinfo.af_inet.mc_loop)
+#ifdef CONFIG_IP_MROUTE
+		/* Small optimization: do not loopback not local frames,
+		   which returned after forwarding; they will be  dropped
+		   by ip_mr_input in any case.
+		   Note, that local frames are looped back to be delivered
+		   to local recipients.
+
+		   This check is duplicated in ip_mr_input at the moment.
+		 */
+		    && ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))
+#endif
+		) {
+			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+			if (newskb)
+				NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
+					newskb->dev, 
+					ip_dev_loopback_xmit);
+		}
+
+		/* Multicasts with ttl 0 must not go beyond the host */
+
+		if (skb->nh.iph->ttl == 0) {
+			kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	if (rt->rt_flags&RTCF_BROADCAST) {
+		struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+		if (newskb)
+			NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
+				newskb->dev, ip_dev_loopback_xmit);
+	}
+
+	return ip_finish_output(skb);
+}
+
+int ip_output(struct sk_buff *skb)
+{
+#ifdef CONFIG_IP_ROUTE_NAT
+	struct rtable *rt = (struct rtable*)skb->dst;
+#endif
+
+	IP_INC_STATS(IpOutRequests);
+
+#ifdef CONFIG_IP_ROUTE_NAT
+	if (rt->rt_flags&RTCF_NAT)
+		ip_do_nat(skb);
+#endif
+
+	return ip_finish_output(skb);
+}
+
+/* Queues a packet to be sent, and starts the transmitter if necessary.  
+ * This routine also needs to put in the total length and compute the 
+ * checksum.  We use to do this in two stages, ip_build_header() then
+ * this, but that scheme created a mess when routes disappeared etc.
+ * So we do it all here, and the TCP send engine has been changed to
+ * match. (No more unroutable FIN disasters, etc. wheee...)  This will
+ * most likely make other reliable transport layers above IP easier
+ * to implement under Linux.
+ */
+static inline int ip_queue_xmit2(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct rtable *rt = (struct rtable *)skb->dst;
+	struct net_device *dev;
+	struct iphdr *iph = skb->nh.iph;
+
+	dev = rt->u.dst.dev;
+
+	/* This can happen when the transport layer has segments queued
+	 * with a cached route, and by the time we get here things are
+	 * re-routed to a device with a different MTU than the original
+	 * device.  Sick, but we must cover it.
+	 */
+	if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);
+		kfree_skb(skb);
+		if (skb2 == NULL)
+			return -ENOMEM;
+		if (sk)
+			skb_set_owner_w(skb2, sk);
+		skb = skb2;
+		iph = skb->nh.iph;
+	}
+
+	if (skb->len > rt->u.dst.pmtu)
+		goto fragment;
+
+	ip_select_ident(iph, &rt->u.dst, sk);
+
+	/* Add an IP checksum. */
+	ip_send_check(iph);
+
+	skb->priority = sk->priority;
+	return skb->dst->output(skb);
+
+fragment:
+	if (ip_dont_fragment(sk, &rt->u.dst)) {
+		/* Reject packet ONLY if TCP might fragment
+		 * it itself, if were careful enough.
+		 */
+		NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big (len[%u] pmtu[%u]) to self\n",
+				skb->len, rt->u.dst.pmtu));
+
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+			  htonl(rt->u.dst.pmtu));
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+	ip_select_ident(iph, &rt->u.dst, sk);
+	if (skb->ip_summed == CHECKSUM_HW &&
+	    (skb = skb_checksum_help(skb)) == NULL)
+		return -ENOMEM;
+	return ip_fragment(skb, skb->dst->output);
+}
+
+int ip_queue_xmit(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct ip_options *opt = sk->protinfo.af_inet.opt;
+	struct rtable *rt;
+	struct iphdr *iph;
+
+	/* Skip all of this if the packet is already routed,
+	 * f.e. by something like SCTP.
+	 */
+	rt = (struct rtable *) skb->dst;
+	if (rt != NULL)
+		goto packet_routed;
+
+	/* Make sure we can route this packet. */
+	rt = (struct rtable *)__sk_dst_check(sk, 0);
+	if (rt == NULL) {
+		u32 daddr;
+
+		/* Use correct destination address if we have options. */
+		daddr = sk->daddr;
+		if(opt && opt->srr)
+			daddr = opt->faddr;
+
+		/* If this fails, retransmit mechanism of transport layer will
+		 * keep trying until route appears or the connection times itself
+		 * out.
+		 */
+		if (ip_route_output(&rt, daddr, sk->saddr,
+				    RT_CONN_FLAGS(sk),
+				    sk->bound_dev_if))
+			goto no_route;
+		__sk_dst_set(sk, &rt->u.dst);
+		sk->route_caps = rt->u.dst.dev->features;
+	}
+	skb->dst = dst_clone(&rt->u.dst);
+
+packet_routed:
+	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+		goto no_route;
+
+	/* OK, we know where to send it, allocate and build IP header. */
+	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff));
+	iph->tot_len = htons(skb->len);
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		iph->frag_off = __constant_htons(IP_DF);
+	else
+		iph->frag_off = 0;
+	iph->ttl      = sk->protinfo.af_inet.ttl;
+	iph->protocol = sk->protocol;
+	iph->saddr    = rt->rt_src;
+	iph->daddr    = rt->rt_dst;
+	skb->nh.iph   = iph;
+	/* Transport layer set skb->h.foo itself. */
+
+	if(opt && opt->optlen) {
+		iph->ihl += opt->optlen >> 2;
+		ip_options_build(skb, opt, sk->daddr, rt, 0);
+	}
+
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		       ip_queue_xmit2);
+
+no_route:
+	IP_INC_STATS(IpOutNoRoutes);
+	kfree_skb(skb);
+	return -EHOSTUNREACH;
+}
+
+/*
+ *	Build and send a packet, with as little as one copy
+ *
+ *	Doesn't care much about ip options... option length can be
+ *	different for fragment at 0 and other fragments.
+ *
+ *	Note that the fragment at the highest offset is sent first,
+ *	so the getfrag routine can fill in the TCP/UDP checksum header
+ *	field in the last fragment it sends... actually it also helps
+ * 	the reassemblers, they can put most packets in at the head of
+ *	the fragment queue, and they know the total size in advance. This
+ *	last feature will measurably improve the Linux fragment handler one
+ *	day.
+ *
+ *	The callback has five args, an arbitrary pointer (copy of frag),
+ *	the source IP address (may depend on the routing table), the 
+ *	destination address (char *), the offset to copy from, and the
+ *	length to be copied.
+ */
+
+static int ip_build_xmit_slow(struct sock *sk,
+		  int getfrag (const void *,
+			       char *,
+			       unsigned int,	
+			       unsigned int),
+		  const void *frag,
+		  unsigned length,
+		  struct ipcm_cookie *ipc,
+		  struct rtable *rt,
+		  int flags)
+{
+	unsigned int fraglen, maxfraglen, fragheaderlen;
+	int err;
+	int offset, mf;
+	int mtu;
+	u16 id;
+
+	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
+	int nfrags=0;
+	struct ip_options *opt = ipc->opt;
+	int df = 0;
+
+	mtu = rt->u.dst.pmtu;
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		df = htons(IP_DF);
+
+	length -= sizeof(struct iphdr);
+
+	if (opt) {
+		fragheaderlen = sizeof(struct iphdr) + opt->optlen;
+		maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
+	} else {
+		fragheaderlen = sizeof(struct iphdr);
+
+		/*
+		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
+		 *	out the size of the frames to send.
+		 */
+
+		maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
+	}
+
+	if (length + fragheaderlen > 0xFFFF) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
+		return -EMSGSIZE;
+	}
+
+	/*
+	 *	Start at the end of the frame by handling the remainder.
+	 */
+
+	offset = length - (length % (maxfraglen - fragheaderlen));
+
+	/*
+	 *	Amount of memory to allocate for final fragment.
+	 */
+
+	fraglen = length - offset + fragheaderlen;
+
+	if (length-offset==0) {
+		fraglen = maxfraglen;
+		offset -= maxfraglen-fragheaderlen;
+	}
+
+	/*
+	 *	The last fragment will not have MF (more fragments) set.
+	 */
+
+	mf = 0;
+
+	/*
+	 *	Don't fragment packets for path mtu discovery.
+	 */
+
+	if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) { 
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
+ 		return -EMSGSIZE;
+	}
+	if (flags&MSG_PROBE)
+		goto out;
+
+	/*
+	 *	Begin outputting the bytes.
+	 */
+
+	id = sk->protinfo.af_inet.id++;
+
+	do {
+		char *data;
+		struct sk_buff * skb;
+
+		/*
+		 *	Get the memory we require with some space left for alignment.
+		 */
+		if (!(flags & MSG_DONTWAIT) || nfrags == 0) {
+			skb = sock_alloc_send_skb(sk, fraglen + hh_len + 15,
+						  (flags & MSG_DONTWAIT), &err);
+		} else {
+			/* On a non-blocking write, we check for send buffer
+			 * usage on the first fragment only.
+			 */
+			skb = sock_wmalloc(sk, fraglen + hh_len + 15, 1,
+					   sk->allocation);
+			if (!skb)
+				err = -ENOBUFS;
+		}
+		if (skb == NULL)
+			goto error;
+
+		/*
+		 *	Fill in the control structures
+		 */
+
+		skb->priority = sk->priority;
+		skb->dst = dst_clone(&rt->u.dst);
+		skb_reserve(skb, hh_len);
+
+		/*
+		 *	Find where to start putting bytes.
+		 */
+
+		data = skb_put(skb, fraglen);
+		skb->nh.iph = (struct iphdr *)data;
+
+		/*
+		 *	Only write IP header onto non-raw packets 
+		 */
+
+		{
+			struct iphdr *iph = (struct iphdr *)data;
+
+			iph->version = 4;
+			iph->ihl = 5;
+			if (opt) {
+				iph->ihl += opt->optlen>>2;
+				ip_options_build(skb, opt,
+						 ipc->addr, rt, offset);
+			}
+			iph->tos = sk->protinfo.af_inet.tos;
+			iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
+			iph->frag_off = htons(offset>>3)|mf|df;
+			iph->id = id;
+			if (!mf) {
+				if (offset || !df) {
+					/* Select an unpredictable ident only
+					 * for packets without DF or having
+					 * been fragmented.
+					 */
+					__ip_select_ident(iph, &rt->u.dst);
+					id = iph->id;
+				}
+
+				/*
+				 *	Any further fragments will have MF set.
+				 */
+				mf = htons(IP_MF);
+			}
+			if (rt->rt_type == RTN_MULTICAST)
+				iph->ttl = sk->protinfo.af_inet.mc_ttl;
+			else
+				iph->ttl = sk->protinfo.af_inet.ttl;
+			iph->protocol = sk->protocol;
+			iph->check = 0;
+			iph->saddr = rt->rt_src;
+			iph->daddr = rt->rt_dst;
+			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+			data += iph->ihl*4;
+		}
+
+		/*
+		 *	User data callback
+		 */
+
+		if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
+			err = -EFAULT;
+			kfree_skb(skb);
+			goto error;
+		}
+
+		offset -= (maxfraglen-fragheaderlen);
+		fraglen = maxfraglen;
+
+		nfrags++;
+
+		err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, 
+			      skb->dst->dev, output_maybe_reroute);
+		if (err) {
+			if (err > 0)
+				err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;
+			if (err)
+				goto error;
+		}
+	} while (offset >= 0);
+
+	if (nfrags>1)
+		ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
+out:
+	return 0;
+
+error:
+	IP_INC_STATS(IpOutDiscards);
+	if (nfrags>1)
+		ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
+	return err; 
+}
+
+/*
+ *	Fast path for unfragmented packets.
+ */
+int ip_build_xmit(struct sock *sk, 
+		  int getfrag (const void *,
+			       char *,
+			       unsigned int,	
+			       unsigned int),
+		  const void *frag,
+		  unsigned length,
+		  struct ipcm_cookie *ipc,
+		  struct rtable *rt,
+		  int flags)
+{
+	int err;
+	struct sk_buff *skb;
+	int df;
+	struct iphdr *iph;
+
+	/*
+	 *	Try the simple case first. This leaves fragmented frames, and by
+	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
+	 */
+
+	if (!sk->protinfo.af_inet.hdrincl) {
+		length += sizeof(struct iphdr);
+
+		/*
+		 * 	Check for slow path.
+		 */
+		if (length > rt->u.dst.pmtu || ipc->opt != NULL)  
+			return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags); 
+	} else {
+		if (length > rt->u.dst.dev->mtu) {
+			ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
+			return -EMSGSIZE;
+		}
+	}
+	if (flags&MSG_PROBE)
+		goto out;
+
+	/*
+	 *	Do path mtu discovery if needed.
+	 */
+	df = 0;
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		df = htons(IP_DF);
+
+	/* 
+	 *	Fast path for unfragmented frames without options. 
+	 */ 
+	{
+	int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
+
+	skb = sock_alloc_send_skb(sk, length+hh_len+15,
+				  flags&MSG_DONTWAIT, &err);
+	if(skb==NULL)
+		goto error; 
+	skb_reserve(skb, hh_len);
+	}
+
+	skb->priority = sk->priority;
+	skb->dst = dst_clone(&rt->u.dst);
+
+	skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
+
+	if(!sk->protinfo.af_inet.hdrincl) {
+		iph->version=4;
+		iph->ihl=5;
+		iph->tos=sk->protinfo.af_inet.tos;
+		iph->tot_len = htons(length);
+		iph->frag_off = df;
+		iph->ttl=sk->protinfo.af_inet.mc_ttl;
+		ip_select_ident(iph, &rt->u.dst, sk);
+		if (rt->rt_type != RTN_MULTICAST)
+			iph->ttl=sk->protinfo.af_inet.ttl;
+		iph->protocol=sk->protocol;
+		iph->saddr=rt->rt_src;
+		iph->daddr=rt->rt_dst;
+		iph->check=0;
+		iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+		err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
+	}
+	else
+		err = getfrag(frag, (void *)iph, 0, length);
+
+	if (err)
+		goto error_fault;
+
+	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		      output_maybe_reroute);
+	if (err > 0)
+		err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;
+	if (err)
+		goto error;
+out:
+	return 0;
+
+error_fault:
+	err = -EFAULT;
+	kfree_skb(skb);
+error:
+	IP_INC_STATS(IpOutDiscards);
+	return err; 
+}
+
+/*
+ *	This IP datagram is too large to be sent in one piece.  Break it up into
+ *	smaller pieces (each of size equal to IP header plus
+ *	a block of the data of the original IP data part) that will yet fit in a
+ *	single device frame, and queue such a frame for sending.
+ *
+ *	Yes this is inefficient, feel free to submit a quicker one.
+ */
+
+int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+{
+	struct iphdr *iph;
+	int raw = 0;
+	int ptr;
+	struct net_device *dev;
+	struct sk_buff *skb2;
+	unsigned int mtu, hlen, left, len; 
+	int offset;
+	int not_last_frag;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	int err = 0;
+
+	dev = rt->u.dst.dev;
+
+	/*
+	 *	Point into the IP datagram header.
+	 */
+
+	iph = skb->nh.iph;
+
+	/*
+	 *	Setup starting values.
+	 */
+
+	hlen = iph->ihl * 4;
+	left = skb->len - hlen;		/* Space per frame */
+	mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
+	ptr = raw + hlen;		/* Where to start from */
+
+	/*
+	 *	Fragment the datagram.
+	 */
+
+	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+	not_last_frag = iph->frag_off & htons(IP_MF);
+
+	/*
+	 *	Keep copying data until we run out.
+	 */
+
+	while(left > 0)	{
+		len = left;
+		/* IF: it doesn't fit, use 'mtu' - the data space left */
+		if (len > mtu)
+			len = mtu;
+		/* IF: we are not sending upto and including the packet end
+		   then align the next start on an eight byte boundary */
+		if (len < left)	{
+			len &= ~7;
+		}
+		/*
+		 *	Allocate buffer.
+		 */
+
+		if ((skb2 = alloc_skb(len+hlen+dev->hard_header_len+15,GFP_ATOMIC)) == NULL) {
+			NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		/*
+		 *	Set up data on packet
+		 */
+
+		skb2->pkt_type = skb->pkt_type;
+		skb2->priority = skb->priority;
+		skb_reserve(skb2, (dev->hard_header_len+15)&~15);
+		skb_put(skb2, len + hlen);
+		skb2->nh.raw = skb2->data;
+		skb2->h.raw = skb2->data + hlen;
+		skb2->protocol = skb->protocol;
+		skb2->security = skb->security;
+
+		/*
+		 *	Charge the memory for the fragment to any owner
+		 *	it might possess
+		 */
+
+		if (skb->sk)
+			skb_set_owner_w(skb2, skb->sk);
+		skb2->dst = dst_clone(skb->dst);
+		skb2->dev = skb->dev;
+		skb2->physindev = skb->physindev;
+		skb2->physoutdev = skb->physoutdev;
+
+		/*
+		 *	Copy the packet header into the new buffer.
+		 */
+
+		memcpy(skb2->nh.raw, skb->data, hlen);
+
+		/*
+		 *	Copy a block of the IP datagram.
+		 */
+		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))
+			BUG();
+		left -= len;
+
+		/*
+		 *	Fill in the new header fields.
+		 */
+		iph = skb2->nh.iph;
+		iph->frag_off = htons((offset >> 3));
+
+		/* ANK: dirty, but effective trick. Upgrade options only if
+		 * the segment to be fragmented was THE FIRST (otherwise,
+		 * options are already fixed) and make it ONCE
+		 * on the initial skb, so that all the following fragments
+		 * will inherit fixed options.
+		 */
+		if (offset == 0)
+			ip_options_fragment(skb);
+
+		/* Copy the flags to each fragment. */
+		IPCB(skb2)->flags = IPCB(skb)->flags;
+
+		/*
+		 *	Added AC : If we are fragmenting a fragment that's not the
+		 *		   last fragment then keep MF on each bit
+		 */
+		if (left > 0 || not_last_frag)
+			iph->frag_off |= htons(IP_MF);
+		ptr += len;
+		offset += len;
+
+#ifdef CONFIG_NET_SCHED
+		skb2->tc_index = skb->tc_index;
+#endif
+#ifdef CONFIG_NETFILTER
+		skb2->nfmark = skb->nfmark;
+		/* Connection association is same as pre-frag packet */
+		skb2->nfct = skb->nfct;
+		nf_conntrack_get(skb2->nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+		skb2->nf_debug = skb->nf_debug;
+#endif
+#endif
+
+		/*
+		 *	Put this fragment into the sending queue.
+		 */
+
+		IP_INC_STATS(IpFragCreates);
+
+		iph->tot_len = htons(len + hlen);
+
+		ip_send_check(iph);
+
+		// for bridge-netfilter
+		memcpy(skb2->data - 16, skb->data - 16, 16);
+
+		err = output(skb2);
+		if (err)
+			goto fail;
+	}
+	kfree_skb(skb);
+	IP_INC_STATS(IpFragOKs);
+	return err;
+
+fail:
+	kfree_skb(skb); 
+	IP_INC_STATS(IpFragFails);
+	return err;
+}
+
+/*
+ *	Fetch data from kernel space and fill in checksum if needed.
+ */
+static int ip_reply_glue_bits(const void *dptr, char *to, unsigned int offset, 
+			      unsigned int fraglen)
+{
+        struct ip_reply_arg *dp = (struct ip_reply_arg*)dptr;
+	u16 *pktp = (u16 *)to;
+	struct iovec *iov; 
+	int len; 
+	int hdrflag = 1; 
+
+	iov = &dp->iov[0]; 
+	if (offset >= iov->iov_len) { 
+		offset -= iov->iov_len;
+		iov++; 
+		hdrflag = 0; 
+	}
+	len = iov->iov_len - offset;
+	if (fraglen > len) { /* overlapping. */ 
+		dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,
+					     dp->csum);
+		offset = 0;
+		fraglen -= len; 
+		to += len; 
+		iov++;
+	}
+
+	dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, fraglen, 
+					     dp->csum); 
+
+	if (hdrflag && dp->csumoffset)
+		*(pktp + dp->csumoffset) = csum_fold(dp->csum); /* fill in checksum */
+	return 0;	       
+}
+
+/* 
+ *	Generic function to send a packet as reply to another packet.
+ *	Used to send TCP resets so far. ICMP should use this function too.
+ *
+ *	Should run single threaded per socket because it uses the sock 
+ *     	structure to pass arguments.
+ */
+void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
+		   unsigned int len)
+{
+	struct {
+		struct ip_options	opt;
+		char			data[40];
+	} replyopts;
+	struct ipcm_cookie ipc;
+	u32 daddr;
+	struct rtable *rt = (struct rtable*)skb->dst;
+
+	if (ip_options_echo(&replyopts.opt, skb))
+		return;
+
+	daddr = ipc.addr = rt->rt_src;
+	ipc.opt = NULL;
+
+	if (replyopts.opt.optlen) {
+		ipc.opt = &replyopts.opt;
+
+		if (ipc.opt->srr)
+			daddr = replyopts.opt.faddr;
+	}
+
+	if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
+		return;
+
+	/* And let IP do all the hard work.
+
+	   This chunk is not reenterable, hence spinlock.
+	   Note that it uses the fact, that this function is called
+	   with locally disabled BH and that sk cannot be already spinlocked.
+	 */
+	bh_lock_sock(sk);
+	sk->protinfo.af_inet.tos = skb->nh.iph->tos;
+	sk->priority = skb->priority;
+	sk->protocol = skb->nh.iph->protocol;
+	ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT);
+	bh_unlock_sock(sk);
+
+	ip_rt_put(rt);
+}
+
+/*
+ *	IP protocol layer initialiser
+ */
+
+static struct packet_type ip_packet_type =
+{
+	__constant_htons(ETH_P_IP),
+	NULL,	/* All devices */
+	ip_rcv,
+	(void*)1,
+	NULL,
+};
+
+/*
+ *	IP registers the packet type and then calls the subprotocol initialisers
+ */
+
+void __init ip_init(void)
+{
+	dev_add_pack(&ip_packet_type);
+
+	ip_rt_init();
+	inet_initpeers();
+
+#ifdef CONFIG_IP_MULTICAST
+	proc_net_create("igmp", 0, ip_mc_procinfo);
+#endif
+}
diff --git a/br-nf-bds/linux/net/ipv4/netfilter/ip_tables.c b/br-nf-bds/linux/net/ipv4/netfilter/ip_tables.c
new file mode 100644
index 0000000..6c203eb
--- /dev/null
+++ b/br-nf-bds/linux/net/ipv4/netfilter/ip_tables.c
@@ -0,0 +1,1812 @@
+/*
+ * Packet matching code.
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * Copyright (C) 2009-2002 Netfilter core team <coreteam@netfilter.org>
+ *
+ * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
+ * 	- increase module usage count as soon as we have rules inside
+ * 	  a table
+ */
+#include <linux/config.h>
+#include <linux/cache.h>
+#include <linux/skbuff.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/*#define DEBUG_IP_FIREWALL*/
+/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+/*#define DEBUG_IP_FIREWALL_USER*/
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf(format, args...)  printk(format , ## args)
+#else
+#define dprintf(format, args...)
+#endif
+
+#ifdef DEBUG_IP_FIREWALL_USER
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#define IP_NF_ASSERT(x)						\
+do {								\
+	if (!(x))						\
+		printk("IP_NF_ASSERT: %s:%s:%u\n",		\
+		       __FUNCTION__, __FILE__, __LINE__);	\
+} while(0)
+#else
+#define IP_NF_ASSERT(x)
+#endif
+#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);
+
+/* Must have mutex */
+#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+/* All the better to debug you with... */
+#define static
+#define inline
+#endif
+
+/* Locking is simple: we assume at worst case there will be one packet
+   in user context and one from bottom halves (or soft irq if Alexey's
+   softnet patch was applied).
+
+   We keep a set of rules for each CPU, so we can avoid write-locking
+   them; doing a readlock_bh() stops packets coming through if we're
+   in user context.
+
+   To be cache friendly on SMP, we arrange them like so:
+   [ n-entries ]
+   ... cache-align padding ...
+   [ n-entries ]
+
+   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;
+	/* Initial number of entries. Needed for module usage count */
+	unsigned int initial_entries;
+
+	/* 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] ____cacheline_aligned;
+};
+
+static LIST_HEAD(ipt_target);
+static LIST_HEAD(ipt_match);
+static LIST_HEAD(ipt_tables);
+#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
+
+#ifdef CONFIG_SMP
+#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
+#else
+#define TABLE_OFFSET(t,p) 0
+#endif
+
+#if 0
+#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
+#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
+#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
+#endif
+
+/* Returns whether matches rule or not. */
+static inline int
+ip_packet_match(const struct iphdr *ip,
+		const char *indev,
+		const char *physindev,
+		const char *outdev,
+		const char *physoutdev,
+		const struct ipt_ip *ipinfo,
+		int isfrag)
+{
+	size_t i;
+	unsigned long ret, ret2;
+
+#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+
+	if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
+		  IPT_INV_SRCIP)
+	    || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
+		     IPT_INV_DSTIP)) {
+		dprintf("Source or dest mismatch.\n");
+
+		dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
+			NIPQUAD(ip->saddr),
+			NIPQUAD(ipinfo->smsk.s_addr),
+			NIPQUAD(ipinfo->src.s_addr),
+			ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
+		dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
+			NIPQUAD(ip->daddr),
+			NIPQUAD(ipinfo->dmsk.s_addr),
+			NIPQUAD(ipinfo->dst.s_addr),
+			ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
+		return 0;
+	}
+
+	/* Look for ifname matches; this should unroll nicely. */
+	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret |= (((const unsigned long *)indev)[i]
+			^ ((const unsigned long *)ipinfo->iniface)[i])
+			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+	}
+
+	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret2 |= (((const unsigned long *)physindev)[i]
+			^ ((const unsigned long *)ipinfo->iniface)[i])
+			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+	}
+
+	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+		dprintf("VIA in mismatch (%s vs %s).%s\n",
+			indev, ipinfo->iniface,
+			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+		return 0;
+	}
+
+	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret |= (((const unsigned long *)outdev)[i]
+			^ ((const unsigned long *)ipinfo->outiface)[i])
+			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+	}
+
+	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret2 |= (((const unsigned long *)physoutdev)[i]
+			^ ((const unsigned long *)ipinfo->outiface)[i])
+			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+	}
+
+	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+		dprintf("VIA out mismatch (%s vs %s).%s\n",
+			outdev, ipinfo->outiface,
+			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+		return 0;
+	}
+
+	/* Check specific protocol */
+	if (ipinfo->proto
+	    && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
+		dprintf("Packet protocol %hi does not match %hi.%s\n",
+			ip->protocol, ipinfo->proto,
+			ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
+		return 0;
+	}
+
+	/* If we have a fragment rule but the packet is not a fragment
+	 * then we return zero */
+	if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
+		dprintf("Fragment rule but not fragment.%s\n",
+			ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
+		return 0;
+	}
+
+	return 1;
+}
+
+static inline int
+ip_checkentry(const struct ipt_ip *ip)
+{
+	if (ip->flags & ~IPT_F_MASK) {
+		duprintf("Unknown flag bits set: %08X\n",
+			 ip->flags & ~IPT_F_MASK);
+		return 0;
+	}
+	if (ip->invflags & ~IPT_INV_MASK) {
+		duprintf("Unknown invflag bits set: %08X\n",
+			 ip->invflags & ~IPT_INV_MASK);
+		return 0;
+	}
+	return 1;
+}
+
+static unsigned int
+ipt_error(struct sk_buff **pskb,
+	  unsigned int hooknum,
+	  const struct net_device *in,
+	  const struct net_device *out,
+	  const void *targinfo,
+	  void *userinfo)
+{
+	if (net_ratelimit())
+		printk("ip_tables: error: `%s'\n", (char *)targinfo);
+
+	return NF_DROP;
+}
+
+static inline
+int do_match(struct ipt_entry_match *m,
+	     const struct sk_buff *skb,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     int offset,
+	     const void *hdr,
+	     u_int16_t datalen,
+	     int *hotdrop)
+{
+	/* Stop iteration if it doesn't match */
+	if (!m->u.kernel.match->match(skb, in, out, m->data,
+				      offset, hdr, datalen, hotdrop))
+		return 1;
+	else
+		return 0;
+}
+
+static inline struct ipt_entry *
+get_entry(void *base, unsigned int offset)
+{
+	return (struct ipt_entry *)(base + offset);
+}
+
+/* Returns one of the generic firewall policies, like NF_ACCEPT. */
+unsigned int
+ipt_do_table(struct sk_buff **pskb,
+	     unsigned int hook,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     struct ipt_table *table,
+	     void *userdata)
+{
+	static const char nulldevname[IFNAMSIZ] = { 0 };
+	u_int16_t offset;
+	struct iphdr *ip;
+	void *protohdr;
+	u_int16_t datalen;
+	int hotdrop = 0;
+	/* Initializing verdict to NF_DROP keeps gcc happy. */
+	unsigned int verdict = NF_DROP;
+	const char *indev, *outdev;
+	const char *physindev, *physoutdev;
+	void *table_base;
+	struct ipt_entry *e, *back;
+
+	/* Initialization */
+	ip = (*pskb)->nh.iph;
+	protohdr = (u_int32_t *)ip + ip->ihl;
+	datalen = (*pskb)->len - ip->ihl * 4;
+	indev = in ? in->name : nulldevname;
+	outdev = out ? out->name : nulldevname;
+	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
+	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
+
+	/* We handle fragments by dealing with the first fragment as
+	 * if it was a normal packet.  All other fragments are treated
+	 * normally, except that they will NEVER match rules that ask
+	 * things we don't know, ie. tcp syn flag or ports).  If the
+	 * rule is also a fragment-specific rule, non-fragments won't
+	 * match it. */
+	offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+	read_lock_bh(&table->lock);
+	IP_NF_ASSERT(table->valid_hooks & (1 << hook));
+	table_base = (void *)table->private->entries
+		+ TABLE_OFFSET(table->private,
+			       cpu_number_map(smp_processor_id()));
+	e = get_entry(table_base, table->private->hook_entry[hook]);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	/* Check noone else using our table */
+	if (((struct ipt_entry *)table_base)->comefrom != 0xdead57ac
+	    && ((struct ipt_entry *)table_base)->comefrom != 0xeeeeeeec) {
+		printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
+		       smp_processor_id(),
+		       table->name,
+		       &((struct ipt_entry *)table_base)->comefrom,
+		       ((struct ipt_entry *)table_base)->comefrom);
+	}
+	((struct ipt_entry *)table_base)->comefrom = 0x57acc001;
+#endif
+
+	/* For return from builtin chain */
+	back = get_entry(table_base, table->private->underflow[hook]);
+
+	do {
+		IP_NF_ASSERT(e);
+		IP_NF_ASSERT(back);
+		(*pskb)->nfcache |= e->nfcache;
+		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
+		    &e->ip, offset)) {
+			struct ipt_entry_target *t;
+
+			if (IPT_MATCH_ITERATE(e, do_match,
+					      *pskb, in, out,
+					      offset, protohdr,
+					      datalen, &hotdrop) != 0)
+				goto no_match;
+
+			ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
+
+			t = ipt_get_target(e);
+			IP_NF_ASSERT(t->u.kernel.target);
+			/* Standard target? */
+			if (!t->u.kernel.target->target) {
+				int v;
+
+				v = ((struct ipt_standard_target *)t)->verdict;
+				if (v < 0) {
+					/* Pop from stack? */
+					if (v != IPT_RETURN) {
+						verdict = (unsigned)(-v) - 1;
+						break;
+					}
+					e = back;
+					back = get_entry(table_base,
+							 back->comefrom);
+					continue;
+				}
+				if (table_base + v
+				    != (void *)e + e->next_offset) {
+					/* Save old back ptr in next entry */
+					struct ipt_entry *next
+						= (void *)e + e->next_offset;
+					next->comefrom
+						= (void *)back - table_base;
+					/* set back pointer to next entry */
+					back = next;
+				}
+
+				e = get_entry(table_base, v);
+			} else {
+				/* Targets which reenter must return
+                                   abs. verdicts */
+#ifdef CONFIG_NETFILTER_DEBUG
+				((struct ipt_entry *)table_base)->comefrom
+					= 0xeeeeeeec;
+#endif
+				verdict = t->u.kernel.target->target(pskb,
+								     hook,
+								     in, out,
+								     t->data,
+								     userdata);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+				if (((struct ipt_entry *)table_base)->comefrom
+				    != 0xeeeeeeec
+				    && verdict == IPT_CONTINUE) {
+					printk("Target %s reentered!\n",
+					       t->u.kernel.target->name);
+					verdict = NF_DROP;
+				}
+				((struct ipt_entry *)table_base)->comefrom
+					= 0x57acc001;
+#endif
+				/* Target might have changed stuff. */
+				ip = (*pskb)->nh.iph;
+				protohdr = (u_int32_t *)ip + ip->ihl;
+				datalen = (*pskb)->len - ip->ihl * 4;
+
+				if (verdict == IPT_CONTINUE)
+					e = (void *)e + e->next_offset;
+				else
+					/* Verdict */
+					break;
+			}
+		} else {
+
+		no_match:
+			e = (void *)e + e->next_offset;
+		}
+	} while (!hotdrop);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	((struct ipt_entry *)table_base)->comefrom = 0xdead57ac;
+#endif
+	read_unlock_bh(&table->lock);
+
+#ifdef DEBUG_ALLOW_ALL
+	return NF_ACCEPT;
+#else
+	if (hotdrop)
+		return NF_DROP;
+	else return verdict;
+#endif
+}
+
+/* If it succeeds, returns element and locks mutex */
+static inline void *
+find_inlist_lock_noload(struct list_head *head,
+			const char *name,
+			int *error,
+			struct semaphore *mutex)
+{
+	void *ret;
+
+#if 0
+	duprintf("find_inlist: searching for `%s' in %s.\n",
+		 name, head == &ipt_target ? "ipt_target"
+		 : head == &ipt_match ? "ipt_match"
+		 : head == &ipt_tables ? "ipt_tables" : "UNKNOWN");
+#endif
+
+	*error = down_interruptible(mutex);
+	if (*error != 0)
+		return NULL;
+
+	ret = list_named_find(head, name);
+	if (!ret) {
+		*error = -ENOENT;
+		up(mutex);
+	}
+	return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head,
+		 const char *name,
+		 const char *prefix,
+		 int *error,
+		 struct semaphore *mutex)
+{
+	void *ret;
+
+	ret = find_inlist_lock_noload(head, name, error, mutex);
+	if (!ret) {
+		char modulename[IPT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
+		strcpy(modulename, prefix);
+		strcat(modulename, name);
+		duprintf("find_inlist: loading `%s'.\n", modulename);
+		request_module(modulename);
+		ret = find_inlist_lock_noload(head, name, error, mutex);
+	}
+
+	return ret;
+}
+#endif
+
+static inline struct ipt_table *
+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)
+{
+	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)
+{
+	return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
+		if (((__u32 *)ip)[i])
+			return 0;
+
+	return 1;
+}
+
+/* Figures out from what hook each rule can be called: returns 0 if
+   there are loops.  Puts hook bitmask in comefrom. */
+static int
+mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
+{
+	unsigned int hook;
+
+	/* No recursion; use packet counter to save back ptrs (reset
+	   to 0 as we leave), and comefrom to save source hook bitmask */
+	for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
+		unsigned int pos = newinfo->hook_entry[hook];
+		struct ipt_entry *e
+			= (struct ipt_entry *)(newinfo->entries + pos);
+
+		if (!(valid_hooks & (1 << hook)))
+			continue;
+
+		/* Set initial back pointer. */
+		e->counters.pcnt = pos;
+
+		for (;;) {
+			struct ipt_standard_target *t
+				= (void *)ipt_get_target(e);
+
+			if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
+				printk("iptables: loop hook %u pos %u %08X.\n",
+				       hook, pos, e->comefrom);
+				return 0;
+			}
+			e->comefrom
+				|= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
+
+			/* Unconditional return/END. */
+			if (e->target_offset == sizeof(struct ipt_entry)
+			    && (strcmp(t->target.u.user.name,
+				       IPT_STANDARD_TARGET) == 0)
+			    && t->verdict < 0
+			    && unconditional(&e->ip)) {
+				unsigned int oldpos, size;
+
+				/* Return: backtrack through the last
+				   big jump. */
+				do {
+					e->comefrom ^= (1<<NF_IP_NUMHOOKS);
+#ifdef DEBUG_IP_FIREWALL_USER
+					if (e->comefrom
+					    & (1 << NF_IP_NUMHOOKS)) {
+						duprintf("Back unset "
+							 "on hook %u "
+							 "rule %u\n",
+							 hook, pos);
+					}
+#endif
+					oldpos = pos;
+					pos = e->counters.pcnt;
+					e->counters.pcnt = 0;
+
+					/* We're at the start. */
+					if (pos == oldpos)
+						goto next;
+
+					e = (struct ipt_entry *)
+						(newinfo->entries + pos);
+				} while (oldpos == pos + e->next_offset);
+
+				/* Move along one */
+				size = e->next_offset;
+				e = (struct ipt_entry *)
+					(newinfo->entries + pos + size);
+				e->counters.pcnt = pos;
+				pos += size;
+			} else {
+				int newpos = t->verdict;
+
+				if (strcmp(t->target.u.user.name,
+					   IPT_STANDARD_TARGET) == 0
+				    && newpos >= 0) {
+					/* This a jump; chase it. */
+					duprintf("Jump rule %u -> %u\n",
+						 pos, newpos);
+				} else {
+					/* ... this is a fallthru */
+					newpos = pos + e->next_offset;
+				}
+				e = (struct ipt_entry *)
+					(newinfo->entries + newpos);
+				e->counters.pcnt = pos;
+				pos = newpos;
+			}
+		}
+		next:
+		duprintf("Finished chain %u\n", hook);
+	}
+	return 1;
+}
+
+static inline int
+cleanup_match(struct ipt_entry_match *m, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+
+	if (m->u.kernel.match->destroy)
+		m->u.kernel.match->destroy(m->data,
+					   m->u.match_size - sizeof(*m));
+
+	if (m->u.kernel.match->me)
+		__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
+
+	return 0;
+}
+
+static inline int
+standard_check(const struct ipt_entry_target *t,
+	       unsigned int max_offset)
+{
+	struct ipt_standard_target *targ = (void *)t;
+
+	/* Check standard info. */
+	if (t->u.target_size
+	    != IPT_ALIGN(sizeof(struct ipt_standard_target))) {
+		duprintf("standard_check: target size %u != %u\n",
+			 t->u.target_size,
+			 IPT_ALIGN(sizeof(struct ipt_standard_target)));
+		return 0;
+	}
+
+	if (targ->verdict >= 0
+	    && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
+		duprintf("ipt_standard_check: bad verdict (%i)\n",
+			 targ->verdict);
+		return 0;
+	}
+
+	if (targ->verdict < -NF_MAX_VERDICT - 1) {
+		duprintf("ipt_standard_check: bad negative verdict (%i)\n",
+			 targ->verdict);
+		return 0;
+	}
+	return 1;
+}
+
+static inline int
+check_match(struct ipt_entry_match *m,
+	    const char *name,
+	    const struct ipt_ip *ip,
+	    unsigned int hookmask,
+	    unsigned int *i)
+{
+	int ret;
+	struct ipt_match *match;
+
+	match = 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;
+	}
+	if (match->me)
+		__MOD_INC_USE_COUNT(match->me);
+	m->u.kernel.match = match;
+	up(&ipt_mutex);
+
+	if (m->u.kernel.match->checkentry
+	    && !m->u.kernel.match->checkentry(name, ip, m->data,
+					      m->u.match_size - sizeof(*m),
+					      hookmask)) {
+		if (m->u.kernel.match->me)
+			__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
+		duprintf("ip_tables: check failed for `%s'.\n",
+			 m->u.kernel.match->name);
+		return -EINVAL;
+	}
+
+	(*i)++;
+	return 0;
+}
+
+static struct ipt_target ipt_standard_target;
+
+static inline int
+check_entry(struct ipt_entry *e, const char *name, unsigned int size,
+	    unsigned int *i)
+{
+	struct ipt_entry_target *t;
+	struct ipt_target *target;
+	int ret;
+	unsigned int j;
+
+	if (!ip_checkentry(&e->ip)) {
+		duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+		return -EINVAL;
+	}
+
+	j = 0;
+	ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
+	if (ret != 0)
+		goto cleanup_matches;
+
+	t = ipt_get_target(e);
+	target = 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;
+	}
+	if (target->me)
+		__MOD_INC_USE_COUNT(target->me);
+	t->u.kernel.target = target;
+	up(&ipt_mutex);
+
+	if (t->u.kernel.target == &ipt_standard_target) {
+		if (!standard_check(t, size)) {
+			ret = -EINVAL;
+			goto cleanup_matches;
+		}
+	} else if (t->u.kernel.target->checkentry
+		   && !t->u.kernel.target->checkentry(name, e, t->data,
+						      t->u.target_size
+						      - sizeof(*t),
+						      e->comefrom)) {
+		if (t->u.kernel.target->me)
+			__MOD_DEC_USE_COUNT(t->u.kernel.target->me);
+		duprintf("ip_tables: check failed for `%s'.\n",
+			 t->u.kernel.target->name);
+		ret = -EINVAL;
+		goto cleanup_matches;
+	}
+
+	(*i)++;
+	return 0;
+
+ cleanup_matches:
+	IPT_MATCH_ITERATE(e, cleanup_match, &j);
+	return ret;
+}
+
+static inline int
+check_entry_size_and_hooks(struct ipt_entry *e,
+			   struct ipt_table_info *newinfo,
+			   unsigned char *base,
+			   unsigned char *limit,
+			   const unsigned int *hook_entries,
+			   const unsigned int *underflows,
+			   unsigned int *i)
+{
+	unsigned int h;
+
+	if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
+	    || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+		duprintf("Bad offset %p\n", e);
+		return -EINVAL;
+	}
+
+	if (e->next_offset
+	    < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
+		duprintf("checking: element %p size %u\n",
+			 e, e->next_offset);
+		return -EINVAL;
+	}
+
+	/* Check hooks & underflows */
+	for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+		if ((unsigned char *)e - base == hook_entries[h])
+			newinfo->hook_entry[h] = hook_entries[h];
+		if ((unsigned char *)e - base == underflows[h])
+			newinfo->underflow[h] = underflows[h];
+	}
+
+	/* FIXME: underflows must be unconditional, standard verdicts
+           < 0 (not IPT_RETURN). --RR */
+
+	/* Clear counters and comefrom */
+	e->counters = ((struct ipt_counters) { 0, 0 });
+	e->comefrom = 0;
+
+	(*i)++;
+	return 0;
+}
+
+static inline int
+cleanup_entry(struct ipt_entry *e, unsigned int *i)
+{
+	struct ipt_entry_target *t;
+
+	if (i && (*i)-- == 0)
+		return 1;
+
+	/* Cleanup all matches */
+	IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+	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);
+
+	return 0;
+}
+
+/* Checks and translates the user-supplied table segment (held in
+   newinfo) */
+static int
+translate_table(const char *name,
+		unsigned int valid_hooks,
+		struct ipt_table_info *newinfo,
+		unsigned int size,
+		unsigned int number,
+		const unsigned int *hook_entries,
+		const unsigned int *underflows)
+{
+	unsigned int i;
+	int ret;
+
+	newinfo->size = size;
+	newinfo->number = number;
+
+	/* Init all hooks to impossible value. */
+	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+		newinfo->hook_entry[i] = 0xFFFFFFFF;
+		newinfo->underflow[i] = 0xFFFFFFFF;
+	}
+
+	duprintf("translate_table: size %u\n", newinfo->size);
+	i = 0;
+	/* Walk through entries, checking offsets. */
+	ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				check_entry_size_and_hooks,
+				newinfo,
+				newinfo->entries,
+				newinfo->entries + size,
+				hook_entries, underflows, &i);
+	if (ret != 0)
+		return ret;
+
+	if (i != number) {
+		duprintf("translate_table: %u not %u entries\n",
+			 i, number);
+		return -EINVAL;
+	}
+
+	/* Check hooks all assigned */
+	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+		/* Only hooks which are valid */
+		if (!(valid_hooks & (1 << i)))
+			continue;
+		if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
+			duprintf("Invalid hook entry %u %u\n",
+				 i, hook_entries[i]);
+			return -EINVAL;
+		}
+		if (newinfo->underflow[i] == 0xFFFFFFFF) {
+			duprintf("Invalid underflow %u %u\n",
+				 i, underflows[i]);
+			return -EINVAL;
+		}
+	}
+
+	if (!mark_source_chains(newinfo, valid_hooks))
+		return -ELOOP;
+
+	/* Finally, each sanity check must pass */
+	i = 0;
+	ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				check_entry, name, size, &i);
+
+	if (ret != 0) {
+		IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				  cleanup_entry, &i);
+		return ret;
+	}
+
+	/* And one copy for every other CPU */
+	for (i = 1; i < smp_num_cpus; i++) {
+		memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
+		       newinfo->entries,
+		       SMP_ALIGN(newinfo->size));
+	}
+
+	return ret;
+}
+
+static struct ipt_table_info *
+replace_table(struct ipt_table *table,
+	      unsigned int num_counters,
+	      struct ipt_table_info *newinfo,
+	      int *error)
+{
+	struct ipt_table_info *oldinfo;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	{
+		struct ipt_entry *table_base;
+		unsigned int i;
+
+		for (i = 0; i < smp_num_cpus; i++) {
+			table_base =
+				(void *)newinfo->entries
+				+ TABLE_OFFSET(newinfo, i);
+
+			table_base->comefrom = 0xdead57ac;
+		}
+	}
+#endif
+
+	/* Do the substitution. */
+	write_lock_bh(&table->lock);
+	/* Check inside lock: is the old number correct? */
+	if (num_counters != table->private->number) {
+		duprintf("num_counters != table->private->number (%u/%u)\n",
+			 num_counters, table->private->number);
+		write_unlock_bh(&table->lock);
+		*error = -EAGAIN;
+		return NULL;
+	}
+	oldinfo = table->private;
+	table->private = newinfo;
+	newinfo->initial_entries = oldinfo->initial_entries;
+	write_unlock_bh(&table->lock);
+
+	return oldinfo;
+}
+
+/* Gets counters. */
+static inline int
+add_entry_to_counter(const struct ipt_entry *e,
+		     struct ipt_counters total[],
+		     unsigned int *i)
+{
+	ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+	(*i)++;
+	return 0;
+}
+
+static void
+get_counters(const struct ipt_table_info *t,
+	     struct ipt_counters counters[])
+{
+	unsigned int cpu;
+	unsigned int i;
+
+	for (cpu = 0; cpu < smp_num_cpus; cpu++) {
+		i = 0;
+		IPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+				  t->size,
+				  add_entry_to_counter,
+				  counters,
+				  &i);
+	}
+}
+
+static int
+copy_entries_to_user(unsigned int total_size,
+		     struct ipt_table *table,
+		     void *userptr)
+{
+	unsigned int off, num, countersize;
+	struct ipt_entry *e;
+	struct ipt_counters *counters;
+	int ret = 0;
+
+	/* We need atomic snapshot of counters: rest doesn't change
+	   (other than comefrom, which userspace doesn't care
+	   about). */
+	countersize = sizeof(struct ipt_counters) * table->private->number;
+	counters = vmalloc(countersize);
+
+	if (counters == NULL)
+		return -ENOMEM;
+
+	/* First, sum counters... */
+	memset(counters, 0, countersize);
+	write_lock_bh(&table->lock);
+	get_counters(table->private, counters);
+	write_unlock_bh(&table->lock);
+
+	/* ... then copy entire thing from CPU 0... */
+	if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+		ret = -EFAULT;
+		goto free_counters;
+	}
+
+	/* FIXME: use iterator macros --RR */
+	/* ... then go back and fix counters and names */
+	for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
+		unsigned int i;
+		struct ipt_entry_match *m;
+		struct ipt_entry_target *t;
+
+		e = (struct ipt_entry *)(table->private->entries + off);
+		if (copy_to_user(userptr + off
+				 + offsetof(struct ipt_entry, counters),
+				 &counters[num],
+				 sizeof(counters[num])) != 0) {
+			ret = -EFAULT;
+			goto free_counters;
+		}
+
+		for (i = sizeof(struct ipt_entry);
+		     i < e->target_offset;
+		     i += m->u.match_size) {
+			m = (void *)e + i;
+
+			if (copy_to_user(userptr + off + i
+					 + offsetof(struct ipt_entry_match,
+						    u.user.name),
+					 m->u.kernel.match->name,
+					 strlen(m->u.kernel.match->name)+1)
+			    != 0) {
+				ret = -EFAULT;
+				goto free_counters;
+			}
+		}
+
+		t = ipt_get_target(e);
+		if (copy_to_user(userptr + off + e->target_offset
+				 + offsetof(struct ipt_entry_target,
+					    u.user.name),
+				 t->u.kernel.target->name,
+				 strlen(t->u.kernel.target->name)+1) != 0) {
+			ret = -EFAULT;
+			goto free_counters;
+		}
+	}
+
+ free_counters:
+	vfree(counters);
+	return ret;
+}
+
+static int
+get_entries(const struct ipt_get_entries *entries,
+	    struct ipt_get_entries *uptr)
+{
+	int ret;
+	struct ipt_table *t;
+
+	t = find_table_lock(entries->name, &ret, &ipt_mutex);
+	if (t) {
+		duprintf("t->private->number = %u\n",
+			 t->private->number);
+		if (entries->size == t->private->size)
+			ret = copy_entries_to_user(t->private->size,
+						   t, uptr->entrytable);
+		else {
+			duprintf("get_entries: I've got %u not %u!\n",
+				 t->private->size,
+				 entries->size);
+			ret = -EINVAL;
+		}
+		up(&ipt_mutex);
+	} else
+		duprintf("get_entries: Can't find %s!\n",
+			 entries->name);
+
+	return ret;
+}
+
+static int
+do_replace(void *user, unsigned int len)
+{
+	int ret;
+	struct ipt_replace tmp;
+	struct ipt_table *t;
+	struct ipt_table_info *newinfo, *oldinfo;
+	struct ipt_counters *counters;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	/* Hack: Causes ipchains to give correct error msg --RR */
+	if (len != sizeof(tmp) + tmp.size)
+		return -ENOPROTOOPT;
+
+	/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
+	if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
+		return -ENOMEM;
+
+	newinfo = vmalloc(sizeof(struct ipt_table_info)
+			  + SMP_ALIGN(tmp.size) * smp_num_cpus);
+	if (!newinfo)
+		return -ENOMEM;
+
+	if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+			   tmp.size) != 0) {
+		ret = -EFAULT;
+		goto free_newinfo;
+	}
+
+	counters = vmalloc(tmp.num_counters * sizeof(struct ipt_counters));
+	if (!counters) {
+		ret = -ENOMEM;
+		goto free_newinfo;
+	}
+	memset(counters, 0, tmp.num_counters * sizeof(struct ipt_counters));
+
+	ret = translate_table(tmp.name, tmp.valid_hooks,
+			      newinfo, tmp.size, tmp.num_entries,
+			      tmp.hook_entry, tmp.underflow);
+	if (ret != 0)
+		goto free_newinfo_counters;
+
+	duprintf("ip_tables: Translated table\n");
+
+	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	if (!t)
+		goto free_newinfo_counters_untrans;
+
+	/* You lied! */
+	if (tmp.valid_hooks != t->valid_hooks) {
+		duprintf("Valid hook crap: %08X vs %08X\n",
+			 tmp.valid_hooks, t->valid_hooks);
+		ret = -EINVAL;
+		goto free_newinfo_counters_untrans_unlock;
+	}
+
+	oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
+	if (!oldinfo)
+		goto free_newinfo_counters_untrans_unlock;
+
+	/* Update module usage count based on number of rules */
+	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+		oldinfo->number, oldinfo->initial_entries, newinfo->number);
+	if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
+ 	    (newinfo->number > oldinfo->initial_entries))
+		__MOD_INC_USE_COUNT(t->me);
+	else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
+	 	 (newinfo->number <= oldinfo->initial_entries))
+		__MOD_DEC_USE_COUNT(t->me);
+
+	/* Get the old counters. */
+	get_counters(oldinfo, counters);
+	/* Decrease module usage counts and free resource */
+	IPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
+	vfree(oldinfo);
+	/* Silent error: too late now. */
+	copy_to_user(tmp.counters, counters,
+		     sizeof(struct ipt_counters) * tmp.num_counters);
+	vfree(counters);
+	up(&ipt_mutex);
+	return 0;
+
+ free_newinfo_counters_untrans_unlock:
+	up(&ipt_mutex);
+ free_newinfo_counters_untrans:
+	IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
+ free_newinfo_counters:
+	vfree(counters);
+ free_newinfo:
+	vfree(newinfo);
+	return ret;
+}
+
+/* We're lazy, and add to the first CPU; overflow works its fey magic
+ * and everything is OK. */
+static inline int
+add_counter_to_entry(struct ipt_entry *e,
+		     const struct ipt_counters addme[],
+		     unsigned int *i)
+{
+#if 0
+	duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
+		 *i,
+		 (long unsigned int)e->counters.pcnt,
+		 (long unsigned int)e->counters.bcnt,
+		 (long unsigned int)addme[*i].pcnt,
+		 (long unsigned int)addme[*i].bcnt);
+#endif
+
+	ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
+
+	(*i)++;
+	return 0;
+}
+
+static int
+do_add_counters(void *user, unsigned int len)
+{
+	unsigned int i;
+	struct ipt_counters_info tmp, *paddc;
+	struct ipt_table *t;
+	int ret;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ipt_counters))
+		return -EINVAL;
+
+	paddc = vmalloc(len);
+	if (!paddc)
+		return -ENOMEM;
+
+	if (copy_from_user(paddc, user, len) != 0) {
+		ret = -EFAULT;
+		goto free;
+	}
+
+	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	if (!t)
+		goto free;
+
+	write_lock_bh(&t->lock);
+	if (t->private->number != paddc->num_counters) {
+		ret = -EINVAL;
+		goto unlock_up_free;
+	}
+
+	i = 0;
+	IPT_ENTRY_ITERATE(t->private->entries,
+			  t->private->size,
+			  add_counter_to_entry,
+			  paddc->counters,
+			  &i);
+ unlock_up_free:
+	write_unlock_bh(&t->lock);
+	up(&ipt_mutex);
+ free:
+	vfree(paddc);
+
+	return ret;
+}
+
+static int
+do_ipt_set_ctl(struct sock *sk,	int cmd, void *user, unsigned int len)
+{
+	int ret;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch (cmd) {
+	case IPT_SO_SET_REPLACE:
+		ret = do_replace(user, len);
+		break;
+
+	case IPT_SO_SET_ADD_COUNTERS:
+		ret = do_add_counters(user, len);
+		break;
+
+	default:
+		duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int
+do_ipt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+	int ret;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch (cmd) {
+	case IPT_SO_GET_INFO: {
+		char name[IPT_TABLE_MAXNAMELEN];
+		struct ipt_table *t;
+
+		if (*len != sizeof(struct ipt_getinfo)) {
+			duprintf("length %u != %u\n", *len,
+				 sizeof(struct ipt_getinfo));
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(name, user, sizeof(name)) != 0) {
+			ret = -EFAULT;
+			break;
+		}
+		name[IPT_TABLE_MAXNAMELEN-1] = '\0';
+		t = find_table_lock(name, &ret, &ipt_mutex);
+		if (t) {
+			struct ipt_getinfo info;
+
+			info.valid_hooks = t->valid_hooks;
+			memcpy(info.hook_entry, t->private->hook_entry,
+			       sizeof(info.hook_entry));
+			memcpy(info.underflow, t->private->underflow,
+			       sizeof(info.underflow));
+			info.num_entries = t->private->number;
+			info.size = t->private->size;
+			strcpy(info.name, name);
+
+			if (copy_to_user(user, &info, *len) != 0)
+				ret = -EFAULT;
+			else
+				ret = 0;
+
+			up(&ipt_mutex);
+		}
+	}
+	break;
+
+	case IPT_SO_GET_ENTRIES: {
+		struct ipt_get_entries get;
+
+		if (*len < sizeof(get)) {
+			duprintf("get_entries: %u < %u\n", *len, sizeof(get));
+			ret = -EINVAL;
+		} else if (copy_from_user(&get, user, sizeof(get)) != 0) {
+			ret = -EFAULT;
+		} else if (*len != sizeof(struct ipt_get_entries) + get.size) {
+			duprintf("get_entries: %u != %u\n", *len,
+				 sizeof(struct ipt_get_entries) + get.size);
+			ret = -EINVAL;
+		} else
+			ret = get_entries(&get, user);
+		break;
+	}
+
+	default:
+		duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/* Registration hooks for targets. */
+int
+ipt_register_target(struct ipt_target *target)
+{
+	int ret;
+
+	MOD_INC_USE_COUNT;
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	if (!list_named_insert(&ipt_target, target)) {
+		duprintf("ipt_register_target: `%s' already in list!\n",
+			 target->name);
+		ret = -EINVAL;
+		MOD_DEC_USE_COUNT;
+	}
+	up(&ipt_mutex);
+	return ret;
+}
+
+void
+ipt_unregister_target(struct ipt_target *target)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_target, target);
+	up(&ipt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int
+ipt_register_match(struct ipt_match *match)
+{
+	int ret;
+
+	MOD_INC_USE_COUNT;
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	if (!list_named_insert(&ipt_match, match)) {
+		duprintf("ipt_register_match: `%s' already in list!\n",
+			 match->name);
+		MOD_DEC_USE_COUNT;
+		ret = -EINVAL;
+	}
+	up(&ipt_mutex);
+
+	return ret;
+}
+
+void
+ipt_unregister_match(struct ipt_match *match)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_match, match);
+	up(&ipt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int ipt_register_table(struct ipt_table *table)
+{
+	int ret;
+	struct ipt_table_info *newinfo;
+	static struct ipt_table_info bootstrap
+		= { 0, 0, 0, { 0 }, { 0 }, { } };
+
+	MOD_INC_USE_COUNT;
+	newinfo = vmalloc(sizeof(struct ipt_table_info)
+			  + SMP_ALIGN(table->table->size) * smp_num_cpus);
+	if (!newinfo) {
+		ret = -ENOMEM;
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	memcpy(newinfo->entries, table->table->entries, table->table->size);
+
+	ret = translate_table(table->name, table->valid_hooks,
+			      newinfo, table->table->size,
+			      table->table->num_entries,
+			      table->table->hook_entry,
+			      table->table->underflow);
+	if (ret != 0) {
+		vfree(newinfo);
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		vfree(newinfo);
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+
+	/* Don't autoload: we'd eat our tail... */
+	if (list_named_find(&ipt_tables, table->name)) {
+		ret = -EEXIST;
+		goto free_unlock;
+	}
+
+	/* Simplifies replace_table code. */
+	table->private = &bootstrap;
+	if (!replace_table(table, 0, newinfo, &ret))
+		goto free_unlock;
+
+	duprintf("table->private->number = %u\n",
+		 table->private->number);
+	
+	/* save number of initial entries */
+	table->private->initial_entries = table->private->number;
+
+	table->lock = RW_LOCK_UNLOCKED;
+	list_prepend(&ipt_tables, table);
+
+ unlock:
+	up(&ipt_mutex);
+	return ret;
+
+ free_unlock:
+	vfree(newinfo);
+	MOD_DEC_USE_COUNT;
+	goto unlock;
+}
+
+void ipt_unregister_table(struct ipt_table *table)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_tables, table);
+	up(&ipt_mutex);
+
+	/* Decrease module usage counts and free resources */
+	IPT_ENTRY_ITERATE(table->private->entries, table->private->size,
+			  cleanup_entry, NULL);
+	vfree(table->private);
+	MOD_DEC_USE_COUNT;
+}
+
+/* Returns 1 if the port is matched by the range, 0 otherwise */
+static inline int
+port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
+{
+	int ret;
+
+	ret = (port >= min && port <= max) ^ invert;
+	return ret;
+}
+
+static int
+tcp_find_option(u_int8_t option,
+		const struct tcphdr *tcp,
+		u_int16_t datalen,
+		int invert,
+		int *hotdrop)
+{
+	unsigned int i = sizeof(struct tcphdr);
+	const u_int8_t *opt = (u_int8_t *)tcp;
+
+	duprintf("tcp_match: finding option\n");
+	/* If we don't have the whole header, drop packet. */
+	if (tcp->doff * 4 > datalen) {
+		*hotdrop = 1;
+		return 0;
+	}
+
+	while (i < tcp->doff * 4) {
+		if (opt[i] == option) return !invert;
+		if (opt[i] < 2) i++;
+		else i += opt[i+1]?:1;
+	}
+
+	return invert;
+}
+
+static int
+tcp_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)
+{
+	const struct tcphdr *tcp = hdr;
+	const struct ipt_tcp *tcpinfo = matchinfo;
+
+	/* To quote Alan:
+
+	   Don't allow a fragment of TCP 8 bytes in. Nobody normal
+	   causes this. Its a cracker trying to break in by doing a
+	   flag overwrite to pass the direction checks.
+	*/
+
+	if (offset == 1) {
+		duprintf("Dropping evil TCP offset=1 frag.\n");
+		*hotdrop = 1;
+		return 0;
+	} else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil TCP offset=0 tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* FIXME: Try tcp doff >> packet len against various stacks --RR */
+
+#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+
+	/* Must not be a fragment. */
+	return !offset
+		&& port_match(tcpinfo->spts[0], tcpinfo->spts[1],
+			      ntohs(tcp->source),
+			      !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT))
+		&& port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
+			      ntohs(tcp->dest),
+			      !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT))
+		&& FWINVTCP((((unsigned char *)tcp)[13]
+			     & tcpinfo->flg_mask)
+			    == tcpinfo->flg_cmp,
+			    IPT_TCP_INV_FLAGS)
+		&& (!tcpinfo->option
+		    || tcp_find_option(tcpinfo->option, tcp, datalen,
+				       tcpinfo->invflags
+				       & IPT_TCP_INV_OPTION,
+				       hotdrop));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+tcp_checkentry(const char *tablename,
+	       const struct ipt_ip *ip,
+	       void *matchinfo,
+	       unsigned int matchsize,
+	       unsigned int hook_mask)
+{
+	const struct ipt_tcp *tcpinfo = matchinfo;
+
+	/* Must specify proto == TCP, and no unknown invflags */
+	return ip->proto == IPPROTO_TCP
+		&& !(ip->invflags & IPT_INV_PROTO)
+		&& matchsize == IPT_ALIGN(sizeof(struct ipt_tcp))
+		&& !(tcpinfo->invflags & ~IPT_TCP_INV_MASK);
+}
+
+static int
+udp_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)
+{
+	const struct udphdr *udp = hdr;
+	const struct ipt_udp *udpinfo = matchinfo;
+
+	if (offset == 0 && datalen < sizeof(struct udphdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil UDP tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& port_match(udpinfo->spts[0], udpinfo->spts[1],
+			      ntohs(udp->source),
+			      !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
+		&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
+			      ntohs(udp->dest),
+			      !!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+udp_checkentry(const char *tablename,
+	       const struct ipt_ip *ip,
+	       void *matchinfo,
+	       unsigned int matchinfosize,
+	       unsigned int hook_mask)
+{
+	const struct ipt_udp *udpinfo = matchinfo;
+
+	/* Must specify proto == UDP, and no unknown invflags */
+	if (ip->proto != IPPROTO_UDP || (ip->invflags & IPT_INV_PROTO)) {
+		duprintf("ipt_udp: Protocol %u != %u\n", ip->proto,
+			 IPPROTO_UDP);
+		return 0;
+	}
+	if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_udp))) {
+		duprintf("ipt_udp: matchsize %u != %u\n",
+			 matchinfosize, IPT_ALIGN(sizeof(struct ipt_udp)));
+		return 0;
+	}
+	if (udpinfo->invflags & ~IPT_UDP_INV_MASK) {
+		duprintf("ipt_udp: unknown flags %X\n",
+			 udpinfo->invflags);
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Returns 1 if the type and code is matched by the range, 0 otherwise */
+static inline int
+icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
+		     u_int8_t type, u_int8_t code,
+		     int invert)
+{
+	return (type == test_type && code >= min_code && code <= max_code)
+		^ invert;
+}
+
+static int
+icmp_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)
+{
+	const struct icmphdr *icmp = hdr;
+	const struct ipt_icmp *icmpinfo = matchinfo;
+
+	if (offset == 0 && datalen < 2) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil ICMP tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& icmp_type_code_match(icmpinfo->type,
+					icmpinfo->code[0],
+					icmpinfo->code[1],
+					icmp->type, icmp->code,
+					!!(icmpinfo->invflags&IPT_ICMP_INV));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+icmp_checkentry(const char *tablename,
+	   const struct ipt_ip *ip,
+	   void *matchinfo,
+	   unsigned int matchsize,
+	   unsigned int hook_mask)
+{
+	const struct ipt_icmp *icmpinfo = matchinfo;
+
+	/* Must specify proto == ICMP, and no unknown invflags */
+	return ip->proto == IPPROTO_ICMP
+		&& !(ip->invflags & IPT_INV_PROTO)
+		&& matchsize == IPT_ALIGN(sizeof(struct ipt_icmp))
+		&& !(icmpinfo->invflags & ~IPT_ICMP_INV);
+}
+
+/* The built-in targets: standard (NULL) and error. */
+static struct ipt_target ipt_standard_target
+= { { NULL, NULL }, IPT_STANDARD_TARGET, NULL, NULL, NULL };
+static struct ipt_target ipt_error_target
+= { { NULL, NULL }, IPT_ERROR_TARGET, ipt_error, NULL, NULL };
+
+static struct nf_sockopt_ops ipt_sockopts
+= { { NULL, NULL }, PF_INET, IPT_BASE_CTL, IPT_SO_SET_MAX+1, do_ipt_set_ctl,
+    IPT_BASE_CTL, IPT_SO_GET_MAX+1, do_ipt_get_ctl, 0, NULL  };
+
+static struct ipt_match tcp_matchstruct
+= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
+static struct ipt_match udp_matchstruct
+= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
+static struct ipt_match icmp_matchstruct
+= { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL };
+
+#ifdef CONFIG_PROC_FS
+static inline int print_name(const struct ipt_table *t,
+			     off_t start_offset, char *buffer, int length,
+			     off_t *pos, unsigned int *count)
+{
+	if ((*count)++ >= start_offset) {
+		unsigned int namelen;
+
+		namelen = sprintf(buffer + *pos, "%s\n", t->name);
+		if (*pos + namelen > length) {
+			/* Stop iterating */
+			return 1;
+		}
+		*pos += namelen;
+	}
+	return 0;
+}
+
+static int ipt_get_tables(char *buffer, char **start, off_t offset, int length)
+{
+	off_t pos = 0;
+	unsigned int count = 0;
+
+	if (down_interruptible(&ipt_mutex) != 0)
+		return 0;
+
+	LIST_FIND(&ipt_tables, print_name, struct ipt_table *,
+		  offset, buffer, length, &pos, &count);
+
+	up(&ipt_mutex);
+
+	/* `start' hack - see fs/proc/generic.c line ~105 */
+	*start=(char *)((unsigned long)count-offset);
+	return pos;
+}
+#endif /*CONFIG_PROC_FS*/
+
+static int __init init(void)
+{
+	int ret;
+
+	/* Noone else will be downing sem now, so we won't sleep */
+	down(&ipt_mutex);
+	list_append(&ipt_target, &ipt_standard_target);
+	list_append(&ipt_target, &ipt_error_target);
+	list_append(&ipt_match, &tcp_matchstruct);
+	list_append(&ipt_match, &udp_matchstruct);
+	list_append(&ipt_match, &icmp_matchstruct);
+	up(&ipt_mutex);
+
+	/* Register setsockopt */
+	ret = nf_register_sockopt(&ipt_sockopts);
+	if (ret < 0) {
+		duprintf("Unable to register sockopts.\n");
+		return ret;
+	}
+
+#ifdef CONFIG_PROC_FS
+	{
+	struct proc_dir_entry *proc;
+
+	proc = proc_net_create("ip_tables_names", 0, ipt_get_tables);
+	if (!proc) {
+		nf_unregister_sockopt(&ipt_sockopts);
+		return -ENOMEM;
+	}
+	proc->owner = THIS_MODULE;
+	}
+#endif
+
+	printk("ip_tables: (C) 2000-2002 Netfilter core team\n");
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	nf_unregister_sockopt(&ipt_sockopts);
+#ifdef CONFIG_PROC_FS
+	proc_net_remove("ip_tables_names");
+#endif
+}
+
+EXPORT_SYMBOL(ipt_register_table);
+EXPORT_SYMBOL(ipt_unregister_table);
+EXPORT_SYMBOL(ipt_register_match);
+EXPORT_SYMBOL(ipt_unregister_match);
+EXPORT_SYMBOL(ipt_do_table);
+EXPORT_SYMBOL(ipt_register_target);
+EXPORT_SYMBOL(ipt_unregister_target);
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/linux/net/ipv4/netfilter/ipt_LOG.c b/br-nf-bds/linux/net/ipv4/netfilter/ipt_LOG.c
new file mode 100644
index 0000000..48bb12f
--- /dev/null
+++ b/br-nf-bds/linux/net/ipv4/netfilter/ipt_LOG.c
@@ -0,0 +1,363 @@
+/*
+ * This is a module which is used for logging packets.
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/spinlock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+struct in_device;
+#include <net/route.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct esphdr {
+	__u32   spi;
+}; /* FIXME evil kludge */
+        
+/* Use lock to serialize, so printks don't overlap */
+static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+
+/* One level of recursion won't kill us */
+static void dump_packet(const struct ipt_log_info *info,
+			struct iphdr *iph, unsigned int len, int recurse)
+{
+	void *protoh = (u_int32_t *)iph + iph->ihl;
+	unsigned int datalen = len - iph->ihl * 4;
+
+	/* Important fields:
+	 * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+	/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+	printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
+	       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+
+	/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+	printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+	       ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
+	       iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
+
+	/* Max length: 6 "CE DF MF " */
+	if (ntohs(iph->frag_off) & IP_CE)
+		printk("CE ");
+	if (ntohs(iph->frag_off) & IP_DF)
+		printk("DF ");
+	if (ntohs(iph->frag_off) & IP_MF)
+		printk("MF ");
+
+	/* Max length: 11 "FRAG:65535 " */
+	if (ntohs(iph->frag_off) & IP_OFFSET)
+		printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
+
+	if ((info->logflags & IPT_LOG_IPOPT)
+	    && iph->ihl * 4 != sizeof(struct iphdr)) {
+		unsigned int i;
+
+		/* Max length: 127 "OPT (" 15*4*2chars ") " */
+		printk("OPT (");
+		for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
+			printk("%02X", ((u_int8_t *)iph)[i]);
+		printk(") ");
+	}
+
+	switch (iph->protocol) {
+	case IPPROTO_TCP: {
+		struct tcphdr *tcph = protoh;
+
+		/* Max length: 10 "PROTO=TCP " */
+		printk("PROTO=TCP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*tcph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 20 "SPT=65535 DPT=65535 " */
+		printk("SPT=%u DPT=%u ",
+		       ntohs(tcph->source), ntohs(tcph->dest));
+		/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+		if (info->logflags & IPT_LOG_TCPSEQ)
+			printk("SEQ=%u ACK=%u ",
+			       ntohl(tcph->seq), ntohl(tcph->ack_seq));
+		/* Max length: 13 "WINDOW=65535 " */
+		printk("WINDOW=%u ", ntohs(tcph->window));
+		/* Max length: 9 "RES=0x3F " */
+		printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
+		/* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
+		if (tcph->cwr)
+			printk("CWR ");
+		if (tcph->ece)
+			printk("ECE ");
+		if (tcph->urg)
+			printk("URG ");
+		if (tcph->ack)
+			printk("ACK ");
+		if (tcph->psh)
+			printk("PSH ");
+		if (tcph->rst)
+			printk("RST ");
+		if (tcph->syn)
+			printk("SYN ");
+		if (tcph->fin)
+			printk("FIN ");
+		/* Max length: 11 "URGP=65535 " */
+		printk("URGP=%u ", ntohs(tcph->urg_ptr));
+
+		if ((info->logflags & IPT_LOG_TCPOPT)
+		    && tcph->doff * 4 != sizeof(struct tcphdr)) {
+			unsigned int i;
+
+			/* Max length: 127 "OPT (" 15*4*2chars ") " */
+			printk("OPT (");
+			for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
+				printk("%02X", ((u_int8_t *)tcph)[i]);
+			printk(") ");
+		}
+		break;
+	}
+	case IPPROTO_UDP: {
+		struct udphdr *udph = protoh;
+
+		/* Max length: 10 "PROTO=UDP " */
+		printk("PROTO=UDP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*udph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 20 "SPT=65535 DPT=65535 " */
+		printk("SPT=%u DPT=%u LEN=%u ",
+		       ntohs(udph->source), ntohs(udph->dest),
+		       ntohs(udph->len));
+		break;
+	}
+	case IPPROTO_ICMP: {
+		struct icmphdr *icmph = protoh;
+		static size_t required_len[NR_ICMP_TYPES+1]
+			= { [ICMP_ECHOREPLY] = 4,
+			    [ICMP_DEST_UNREACH]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_SOURCE_QUENCH]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_REDIRECT]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_ECHO] = 4,
+			    [ICMP_TIME_EXCEEDED]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_PARAMETERPROB]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_TIMESTAMP] = 20,
+			    [ICMP_TIMESTAMPREPLY] = 20,
+			    [ICMP_ADDRESS] = 12,
+			    [ICMP_ADDRESSREPLY] = 12 };
+
+		/* Max length: 11 "PROTO=ICMP " */
+		printk("PROTO=ICMP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < 4) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 18 "TYPE=255 CODE=255 " */
+		printk("TYPE=%u CODE=%u ", icmph->type, icmph->code);
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (icmph->type <= NR_ICMP_TYPES
+		    && required_len[icmph->type]
+		    && datalen < required_len[icmph->type]) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		switch (icmph->type) {
+		case ICMP_ECHOREPLY:
+		case ICMP_ECHO:
+			/* Max length: 19 "ID=65535 SEQ=65535 " */
+			printk("ID=%u SEQ=%u ",
+			       ntohs(icmph->un.echo.id),
+			       ntohs(icmph->un.echo.sequence));
+			break;
+
+		case ICMP_PARAMETERPROB:
+			/* Max length: 14 "PARAMETER=255 " */
+			printk("PARAMETER=%u ",
+			       ntohl(icmph->un.gateway) >> 24);
+			break;
+		case ICMP_REDIRECT:
+			/* Max length: 24 "GATEWAY=255.255.255.255 " */
+			printk("GATEWAY=%u.%u.%u.%u ", NIPQUAD(icmph->un.gateway));
+			/* Fall through */
+		case ICMP_DEST_UNREACH:
+		case ICMP_SOURCE_QUENCH:
+		case ICMP_TIME_EXCEEDED:
+			/* Max length: 3+maxlen */
+			if (recurse) {
+				printk("[");
+				dump_packet(info,
+					    (struct iphdr *)(icmph + 1),
+					    datalen-sizeof(struct icmphdr),
+					    0);
+				printk("] ");
+			}
+
+			/* Max length: 10 "MTU=65535 " */
+			if (icmph->type == ICMP_DEST_UNREACH
+			    && icmph->code == ICMP_FRAG_NEEDED)
+				printk("MTU=%u ", ntohs(icmph->un.frag.mtu));
+		}
+		break;
+	}
+	/* Max Length */
+	case IPPROTO_AH:
+	case IPPROTO_ESP: {
+		struct esphdr *esph = protoh;
+		int esp= (iph->protocol==IPPROTO_ESP);
+
+		/* Max length: 10 "PROTO=ESP " */
+		printk("PROTO=%s ",esp? "ESP" : "AH");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*esph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Length: 15 "SPI=0xF1234567 " */
+		printk("SPI=0x%x ", ntohl(esph->spi) );
+		break;
+	}
+	/* Max length: 10 "PROTO 255 " */
+	default:
+		printk("PROTO=%u ", iph->protocol);
+	}
+
+	/* Proto    Max log string length */
+	/* IP:      40+46+6+11+127 = 230 */
+	/* TCP:     10+max(25,20+30+13+9+32+11+127) = 252 */
+	/* UDP:     10+max(25,20) = 35 */
+	/* ICMP:    11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
+	/* ESP:     10+max(25)+15 = 50 */
+	/* AH:      9+max(25)+15 = 49 */
+	/* unknown: 10 */
+
+	/* (ICMP allows recursion one level deep) */
+	/* maxlen =  IP + ICMP +  IP + max(TCP,UDP,ICMP,unknown) */
+	/* maxlen = 230+   91  + 230 + 252 = 803 */
+}
+
+static unsigned int
+ipt_log_target(struct sk_buff **pskb,
+	       unsigned int hooknum,
+	       const struct net_device *in,
+	       const struct net_device *out,
+	       const void *targinfo,
+	       void *userinfo)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	const struct ipt_log_info *loginfo = targinfo;
+	char level_string[4] = "< >";
+
+	level_string[1] = '0' + (loginfo->level % 8);
+	spin_lock_bh(&log_lock);
+	printk(level_string);
+	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
+	if ((*pskb)->physindev && in != (*pskb)->physindev)
+		printk("PHYSIN=%s ", (*pskb)->physindev->name);
+	printk("OUT=%s ", out ? out->name : "");
+	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
+		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
+
+	if (in && !out) {
+		/* MAC logging for input chain only. */
+		printk("MAC=");
+		if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)iph) {
+			int i;
+			unsigned char *p = (*pskb)->mac.raw;
+			for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++)
+				printk("%02x%c", *p,
+				       i==(*pskb)->dev->hard_header_len - 1
+				       ? ' ':':');
+		} else
+			printk(" ");
+	}
+
+	dump_packet(loginfo, iph, (*pskb)->len, 1);
+	printk("\n");
+	spin_unlock_bh(&log_lock);
+
+	return IPT_CONTINUE;
+}
+
+static int ipt_log_checkentry(const char *tablename,
+			      const struct ipt_entry *e,
+			      void *targinfo,
+			      unsigned int targinfosize,
+			      unsigned int hook_mask)
+{
+	const struct ipt_log_info *loginfo = targinfo;
+
+	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_log_info))) {
+		DEBUGP("LOG: targinfosize %u != %u\n",
+		       targinfosize, IPT_ALIGN(sizeof(struct ipt_log_info)));
+		return 0;
+	}
+
+	if (loginfo->level >= 8) {
+		DEBUGP("LOG: level %u >= 8\n", loginfo->level);
+		return 0;
+	}
+
+	if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
+		DEBUGP("LOG: prefix term %i\n",
+		       loginfo->prefix[sizeof(loginfo->prefix)-1]);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_target ipt_log_reg
+= { { NULL, NULL }, "LOG", ipt_log_target, ipt_log_checkentry, NULL, 
+    THIS_MODULE };
+
+static int __init init(void)
+{
+	if (ipt_register_target(&ipt_log_reg))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	ipt_unregister_target(&ipt_log_reg);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/linux2.5/include/linux/netfilter.h b/br-nf-bds/linux2.5/include/linux/netfilter.h
new file mode 100644
index 0000000..b1ae544
--- /dev/null
+++ b/br-nf-bds/linux2.5/include/linux/netfilter.h
@@ -0,0 +1,168 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/if.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#endif
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_MAX_VERDICT NF_REPEAT
+
+/* Generic cache responses from hook functions. */
+#define NFC_ALTERED 0x8000
+#define NFC_UNKNOWN 0x4000
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#ifdef CONFIG_NETFILTER
+
+extern void netfilter_init(void);
+
+/* Largest hook number + 1 */
+#define NF_MAX_HOOKS 8
+
+struct sk_buff;
+struct net_device;
+
+typedef unsigned int nf_hookfn(unsigned int hooknum,
+			       struct sk_buff **skb,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       int (*okfn)(struct sk_buff *));
+
+struct nf_hook_ops
+{
+	struct list_head list;
+
+	/* User fills in from here down. */
+	nf_hookfn *hook;
+	int pf;
+	int hooknum;
+	/* Hooks are ordered in ascending priority. */
+	int priority;
+};
+
+struct nf_sockopt_ops
+{
+	struct list_head list;
+
+	int pf;
+
+	/* Non-inclusive ranges: use 0/0/NULL to never get called. */
+	int set_optmin;
+	int set_optmax;
+	int (*set)(struct sock *sk, int optval, void *user, unsigned int len);
+
+	int get_optmin;
+	int get_optmax;
+	int (*get)(struct sock *sk, int optval, void *user, int *len);
+
+	/* Number of users inside set() or get(). */
+	unsigned int use;
+	struct task_struct *cleanup_task;
+};
+
+/* Each queued (to userspace) skbuff has one of these. */
+struct nf_info
+{
+	/* The ops struct which sent us to userspace. */
+	struct nf_hook_ops *elem;
+	
+	/* If we're sent to userspace, this keeps housekeeping info */
+	int pf;
+	unsigned int hook;
+	struct net_device *indev, *outdev;
+	int (*okfn)(struct sk_buff *);
+};
+                                                                                
+/* Function to register/unregister hook points. */
+int nf_register_hook(struct nf_hook_ops *reg);
+void nf_unregister_hook(struct nf_hook_ops *reg);
+
+/* Functions to register get/setsockopt ranges (non-inclusive).  You
+   need to check permissions yourself! */
+int nf_register_sockopt(struct nf_sockopt_ops *reg);
+void nf_unregister_sockopt(struct nf_sockopt_ops *reg);
+
+extern struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
+
+/* Activate hook; either okfn or kfree_skb called, unless a hook
+   returns NF_STOLEN (in which case, it's up to the hook to deal with
+   the consequences).
+
+   Returns -ERRNO if packet dropped.  Zero means queued, stolen or
+   accepted.
+*/
+
+/* RR:
+   > I don't want nf_hook to return anything because people might forget
+   > about async and trust the return value to mean "packet was ok".
+
+   AK:
+   Just document it clearly, then you can expect some sense from kernel
+   coders :)
+*/
+
+/* This is gross, but inline doesn't cut it for avoiding the function
+   call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+#ifdef CONFIG_NETFILTER_DEBUG
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_THRESH nf_hook_slow
+#else
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+(list_empty(&nf_hooks[(pf)][(hook)])					\
+ ? (okfn)(skb)								\
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
+#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
+(list_empty(&nf_hooks[(pf)][(hook)])					\
+ ? (okfn)(skb)								\
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+#endif
+
+int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+		 struct net_device *indev, struct net_device *outdev,
+		 int (*okfn)(struct sk_buff *), int thresh);
+
+/* Call setsockopt() */
+int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+		  int len);
+int nf_getsockopt(struct sock *sk, int pf, int optval, char *opt,
+		  int *len);
+
+/* Packet queuing */
+typedef int (*nf_queue_outfn_t)(struct sk_buff *skb, 
+                                struct nf_info *info, void *data);
+extern int nf_register_queue_handler(int pf, 
+                                     nf_queue_outfn_t outfn, void *data);
+extern int nf_unregister_queue_handler(int pf);
+extern void nf_reinject(struct sk_buff *skb,
+			struct nf_info *info,
+			unsigned int verdict);
+
+extern void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+extern void nf_dump_skb(int pf, struct sk_buff *skb);
+#endif
+
+/* FIXME: Before cache is ever used, this must be implemented for real. */
+extern void nf_invalidate_cache(int pf);
+
+#else /* !CONFIG_NETFILTER */
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
+#endif /*CONFIG_NETFILTER*/
+
+#endif /*__KERNEL__*/
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/br-nf-bds/linux2.5/include/linux/netfilter_bridge.h b/br-nf-bds/linux2.5/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..b9063ac
--- /dev/null
+++ b/br-nf-bds/linux2.5/include/linux/netfilter_bridge.h
@@ -0,0 +1,61 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter. 
+ */
+
+#include <linux/config.h>
+#include <linux/netfilter.h>
+#include <asm/atomic.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING	4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING		5
+#define NF_BR_NUMHOOKS		6
+
+#define BRNF_PKT_TYPE			0x01
+#define BRNF_BRIDGED_DNAT		0x02
+#define BRNF_DONT_TAKE_PARENT		0x04
+
+enum nf_br_hook_priorities {
+	NF_BR_PRI_FIRST = INT_MIN,
+	NF_BR_PRI_NAT_DST_BRIDGED = -300,
+	NF_BR_PRI_FILTER_BRIDGED = -200,
+	NF_BR_PRI_BRNF = 0,
+	NF_BR_PRI_NAT_DST_OTHER = 100,
+	NF_BR_PRI_FILTER_OTHER = 200,
+	NF_BR_PRI_NAT_SRC = 300,
+	NF_BR_PRI_LAST = INT_MAX,
+};
+
+static inline
+struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
+{
+	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
+
+	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
+		atomic_set(&(*nf_bridge)->use, 1);
+		(*nf_bridge)->mask = 0;
+		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
+	}
+
+	return *nf_bridge;
+}
+
+struct bridge_skb_cb {
+	union {
+		__u32 ipv4;
+	} daddr;
+};
+
+#endif
diff --git a/br-nf-bds/linux2.5/include/linux/netfilter_ipv4.h b/br-nf-bds/linux2.5/include/linux/netfilter_ipv4.h
new file mode 100644
index 0000000..3744c61
--- /dev/null
+++ b/br-nf-bds/linux2.5/include/linux/netfilter_ipv4.h
@@ -0,0 +1,80 @@
+#ifndef __LINUX_IP_NETFILTER_H
+#define __LINUX_IP_NETFILTER_H
+
+/* IPv4-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ */
+
+#include <linux/config.h>
+#include <linux/netfilter.h>
+
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP_SRC		0x0001
+/* Dest IP address. */
+#define NFC_IP_DST		0x0002
+/* Input device. */
+#define NFC_IP_IF_IN		0x0004
+/* Output device. */
+#define NFC_IP_IF_OUT		0x0008
+/* TOS. */
+#define NFC_IP_TOS		0x0010
+/* Protocol. */
+#define NFC_IP_PROTO		0x0020
+/* IP options. */
+#define NFC_IP_OPTIONS		0x0040
+/* Frag & flags. */
+#define NFC_IP_FRAG		0x0080
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP_TCPFLAGS		0x0100
+/* Source port. */
+#define NFC_IP_SRC_PT		0x0200
+/* Dest port. */
+#define NFC_IP_DST_PT		0x0400
+/* Something else about the proto */
+#define NFC_IP_PROTO_UNKNOWN	0x2000
+
+/* IP Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_IP_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_IP_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_IP_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_IP_POST_ROUTING	4
+#define NF_IP_NUMHOOKS		5
+
+enum nf_ip_hook_priorities {
+	NF_IP_PRI_FIRST = INT_MIN,
+	NF_IP_PRI_CONNTRACK = -200,
+	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+	NF_IP_PRI_MANGLE = -150,
+	NF_IP_PRI_NAT_DST = -100,
+	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+	NF_IP_PRI_FILTER = 0,
+	NF_IP_PRI_NAT_SRC = 100,
+	NF_IP_PRI_LAST = INT_MAX,
+};
+
+/* Arguments for setsockopt SOL_IP: */
+/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
+/* 2.2 firewalling (+ masq) went from 64 through 76 */
+/* 2.4 firewalling went 64 through 67. */
+#define SO_ORIGINAL_DST 80
+
+#ifdef __KERNEL__
+#ifdef CONFIG_NETFILTER_DEBUG
+void nf_debug_ip_local_deliver(struct sk_buff *skb);
+void nf_debug_ip_loopback_xmit(struct sk_buff *newskb);
+void nf_debug_ip_finish_output2(struct sk_buff *skb);
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+extern int ip_route_me_harder(struct sk_buff **pskb);
+#endif /*__KERNEL__*/
+
+#endif /*__LINUX_IP_NETFILTER_H*/
diff --git a/br-nf-bds/linux2.5/include/linux/skbuff.h b/br-nf-bds/linux2.5/include/linux/skbuff.h
new file mode 100644
index 0000000..cefecda
--- /dev/null
+++ b/br-nf-bds/linux2.5/include/linux/skbuff.h
@@ -0,0 +1,1173 @@
+/*
+ *	Definitions for the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:
+ *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/cache.h>
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/poll.h>
+#include <linux/net.h>
+
+#define HAVE_ALLOC_SKB		/* For the drivers to know */
+#define HAVE_ALIGNABLE_SKB	/* Ditto 8)		   */
+#define SLAB_SKB 		/* Slabified skbuffs 	   */
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_HW 1
+#define CHECKSUM_UNNECESSARY 2
+
+#define SKB_DATA_ALIGN(X)	(((X) + (SMP_CACHE_BYTES - 1)) & \
+				 ~(SMP_CACHE_BYTES - 1))
+#define SKB_MAX_ORDER(X, ORDER)	(((PAGE_SIZE << (ORDER)) - (X) - \
+				  sizeof(struct skb_shared_info)) & \
+				  ~(SMP_CACHE_BYTES - 1))
+#define SKB_MAX_HEAD(X)		(SKB_MAX_ORDER((X), 0))
+#define SKB_MAX_ALLOC		(SKB_MAX_ORDER(0, 2))
+
+/* A. Checksumming of received packets by device.
+ *
+ *	NONE: device failed to checksum this packet.
+ *		skb->csum is undefined.
+ *
+ *	UNNECESSARY: device parsed packet and wouldbe verified checksum.
+ *		skb->csum is undefined.
+ *	      It is bad option, but, unfortunately, many of vendors do this.
+ *	      Apparently with secret goal to sell you new device, when you
+ *	      will add new protocol to your host. F.e. IPv6. 8)
+ *
+ *	HW: the most generic way. Device supplied checksum of _all_
+ *	    the packet as seen by netif_rx in skb->csum.
+ *	    NOTE: Even if device supports only some protocols, but
+ *	    is able to produce some skb->csum, it MUST use HW,
+ *	    not UNNECESSARY.
+ *
+ * B. Checksumming on output.
+ *
+ *	NONE: skb is checksummed by protocol or csum is not required.
+ *
+ *	HW: device is required to csum packet as seen by hard_start_xmit
+ *	from skb->h.raw to the end and to record the checksum
+ *	at skb->h.raw+skb->csum.
+ *
+ *	Device must show its capabilities in dev->features, set
+ *	at device setup time.
+ *	NETIF_F_HW_CSUM	- it is clever device, it is able to checksum
+ *			  everything.
+ *	NETIF_F_NO_CSUM - loopback or reliable single hop media.
+ *	NETIF_F_IP_CSUM - device is dumb. It is able to csum only
+ *			  TCP/UDP over IPv4. Sigh. Vendors like this
+ *			  way by an unknown reason. Though, see comment above
+ *			  about CHECKSUM_UNNECESSARY. 8)
+ *
+ *	Any questions? No questions, good. 		--ANK
+ */
+
+#ifdef __i386__
+#define NET_CALLER(arg) (*(((void **)&arg) - 1))
+#else
+#define NET_CALLER(arg) __builtin_return_address(0)
+#endif
+
+#ifdef CONFIG_NETFILTER
+struct nf_conntrack {
+	atomic_t use;
+	void (*destroy)(struct nf_conntrack *);
+};
+
+struct nf_ct_info {
+	struct nf_conntrack *master;
+};
+
+struct nf_bridge_info {
+	atomic_t use;
+	struct net_device *physindev;
+	struct net_device *physoutdev;
+	unsigned int mask;
+	unsigned long hh[16 / sizeof(unsigned long)];
+};
+#endif
+
+struct sk_buff_head {
+	/* These two members must be first. */
+	struct sk_buff	*next;
+	struct sk_buff	*prev;
+
+	__u32		qlen;
+	spinlock_t	lock;
+};
+
+struct sk_buff;
+
+/* To allow 64K frame to be packed as single skb without frag_list */
+#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 2)
+
+typedef struct skb_frag_struct skb_frag_t;
+
+struct skb_frag_struct {
+	struct page *page;
+	__u16 page_offset;
+	__u16 size;
+};
+
+/* This data is invariant across clones and lives at
+ * the end of the header data, ie. at skb->end.
+ */
+struct skb_shared_info {
+	atomic_t	dataref;
+	unsigned int	nr_frags;
+	unsigned short	tso_size;
+	unsigned short	tso_segs;
+	struct sk_buff	*frag_list;
+	skb_frag_t	frags[MAX_SKB_FRAGS];
+};
+
+/** 
+ *	struct sk_buff - socket buffer
+ *	@next: Next buffer in list
+ *	@prev: Previous buffer in list
+ *	@list: List we are on
+ *	@sk: Socket we are owned by
+ *	@stamp: Time we arrived
+ *	@dev: Device we arrived on/are leaving by
+ *	@h: Transport layer header
+ *	@nh: Network layer header
+ *	@mac: Link layer header
+ *	@dst: FIXME: Describe this field
+ *	@cb: Control buffer. Free for use by every layer. Put private vars here
+ *	@len: Length of actual data
+ *	@data_len: Data length
+ *	@csum: Checksum
+ *	@__unused: Dead field, may be reused
+ *	@cloned: Head may be cloned (check refcnt to be sure)
+ *	@pkt_type: Packet class
+ *	@ip_summed: Driver fed us an IP checksum
+ *	@priority: Packet queueing priority
+ *	@users: User count - see {datagram,tcp}.c
+ *	@protocol: Packet protocol from driver
+ *	@security: Security level of packet
+ *	@truesize: Buffer size 
+ *	@head: Head of buffer
+ *	@data: Data head pointer
+ *	@tail: Tail pointer
+ *	@end: End pointer
+ *	@destructor: Destruct function
+ *	@nfmark: Can be used for communication between hooks
+ *	@nfcache: Cache info
+ *	@nfct: Associated connection, if any
+ *	@nf_debug: Netfilter debugging
+ *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
+ *	@tc_index: Traffic control index
+ */
+
+struct sk_buff {
+	/* These two members must be first. */
+	struct sk_buff		*next;
+	struct sk_buff		*prev;
+
+	struct sk_buff_head	*list;
+	struct sock		*sk;
+	struct timeval		stamp;
+	struct net_device	*dev;
+
+	union {
+		struct tcphdr	*th;
+		struct udphdr	*uh;
+		struct icmphdr	*icmph;
+		struct igmphdr	*igmph;
+		struct iphdr	*ipiph;
+		unsigned char	*raw;
+	} h;
+
+	union {
+		struct iphdr	*iph;
+		struct ipv6hdr	*ipv6h;
+		struct arphdr	*arph;
+		unsigned char	*raw;
+	} nh;
+
+	union {
+	  	struct ethhdr	*ethernet;
+	  	unsigned char 	*raw;
+	} mac;
+
+	struct  dst_entry	*dst;
+
+	/*
+	 * This is the control buffer. It is free to use for every
+	 * layer. Please put your private variables there. If you
+	 * want to keep them across layers you have to do a skb_clone()
+	 * first. This is owned by whoever has the skb queued ATM.
+	 */
+	char			cb[48];
+
+	unsigned int		len,
+				data_len,
+				csum;
+	unsigned char		__unused,
+				cloned,
+				pkt_type,
+				ip_summed;
+	__u32			priority;
+	atomic_t		users;
+	unsigned short		protocol,
+				security;
+	unsigned int		truesize;
+
+	unsigned char		*head,
+				*data,
+				*tail,
+				*end;
+
+	void			(*destructor)(struct sk_buff *skb);
+#ifdef CONFIG_NETFILTER
+        unsigned long		nfmark;
+	__u32			nfcache;
+	struct nf_ct_info	*nfct;
+#ifdef CONFIG_NETFILTER_DEBUG
+        unsigned int		nf_debug;
+#endif
+	struct nf_bridge_info	*nf_bridge;
+#endif /* CONFIG_NETFILTER */
+#if defined(CONFIG_HIPPI)
+	union {
+		__u32		ifield;
+	} private;
+#endif
+#ifdef CONFIG_NET_SCHED
+       __u32			tc_index;               /* traffic control index */
+#endif
+};
+
+#define SK_WMEM_MAX	65535
+#define SK_RMEM_MAX	65535
+
+#ifdef __KERNEL__
+/*
+ *	Handling routines are only of interest to the kernel
+ */
+#include <linux/slab.h>
+
+#include <asm/system.h>
+
+extern void	       __kfree_skb(struct sk_buff *skb);
+extern struct sk_buff *alloc_skb(unsigned int size, int priority);
+extern void	       kfree_skbmem(struct sk_buff *skb);
+extern struct sk_buff *skb_clone(struct sk_buff *skb, int priority);
+extern struct sk_buff *skb_copy(const struct sk_buff *skb, int priority);
+extern struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask);
+extern int	       pskb_expand_head(struct sk_buff *skb,
+					int nhead, int ntail, int gfp_mask);
+extern struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
+					    unsigned int headroom);
+extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
+				       int newheadroom, int newtailroom,
+				       int priority);
+#define dev_kfree_skb(a)	kfree_skb(a)
+extern void	      skb_over_panic(struct sk_buff *skb, int len,
+				     void *here);
+extern void	      skb_under_panic(struct sk_buff *skb, int len,
+				      void *here);
+
+/* Internal */
+#define skb_shinfo(SKB)		((struct skb_shared_info *)((SKB)->end))
+
+/**
+ *	skb_queue_empty - check if a queue is empty
+ *	@list: queue head
+ *
+ *	Returns true if the queue is empty, false otherwise.
+ */
+static inline int skb_queue_empty(struct sk_buff_head *list)
+{
+	return list->next == (struct sk_buff *)list;
+}
+
+/**
+ *	skb_get - reference buffer
+ *	@skb: buffer to reference
+ *
+ *	Makes another reference to a socket buffer and returns a pointer
+ *	to the buffer.
+ */
+static inline struct sk_buff *skb_get(struct sk_buff *skb)
+{
+	atomic_inc(&skb->users);
+	return skb;
+}
+
+/*
+ * If users == 1, we are the only owner and are can avoid redundant
+ * atomic change.
+ */
+
+/**
+ *	kfree_skb - free an sk_buff
+ *	@skb: buffer to free
+ *
+ *	Drop a reference to the buffer and free it if the usage count has
+ *	hit zero.
+ */
+static inline void kfree_skb(struct sk_buff *skb)
+{
+	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+		__kfree_skb(skb);
+}
+
+/* Use this if you didn't touch the skb state [for fast switching] */
+static inline void kfree_skb_fast(struct sk_buff *skb)
+{
+	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+		kfree_skbmem(skb);
+}
+
+/**
+ *	skb_cloned - is the buffer a clone
+ *	@skb: buffer to check
+ *
+ *	Returns true if the buffer was generated with skb_clone() and is
+ *	one of multiple shared copies of the buffer. Cloned buffers are
+ *	shared data so must not be written to under normal circumstances.
+ */
+static inline int skb_cloned(struct sk_buff *skb)
+{
+	return skb->cloned && atomic_read(&skb_shinfo(skb)->dataref) != 1;
+}
+
+/**
+ *	skb_shared - is the buffer shared
+ *	@skb: buffer to check
+ *
+ *	Returns true if more than one person has a reference to this
+ *	buffer.
+ */
+static inline int skb_shared(struct sk_buff *skb)
+{
+	return atomic_read(&skb->users) != 1;
+}
+
+/**
+ *	skb_share_check - check if buffer is shared and if so clone it
+ *	@skb: buffer to check
+ *	@pri: priority for memory allocation
+ *
+ *	If the buffer is shared the buffer is cloned and the old copy
+ *	drops a reference. A new clone with a single reference is returned.
+ *	If the buffer is not shared the original buffer is returned. When
+ *	being called from interrupt status or with spinlocks held pri must
+ *	be GFP_ATOMIC.
+ *
+ *	NULL is returned on a memory allocation failure.
+ */
+static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
+{
+	if (skb_shared(skb)) {
+		struct sk_buff *nskb = skb_clone(skb, pri);
+		kfree_skb(skb);
+		skb = nskb;
+	}
+	return skb;
+}
+
+/*
+ *	Copy shared buffers into a new sk_buff. We effectively do COW on
+ *	packets to handle cases where we have a local reader and forward
+ *	and a couple of other messy ones. The normal one is tcpdumping
+ *	a packet thats being forwarded.
+ */
+
+/**
+ *	skb_unshare - make a copy of a shared buffer
+ *	@skb: buffer to check
+ *	@pri: priority for memory allocation
+ *
+ *	If the socket buffer is a clone then this function creates a new
+ *	copy of the data, drops a reference count on the old copy and returns
+ *	the new copy with the reference count at 1. If the buffer is not a clone
+ *	the original buffer is returned. When called with a spinlock held or
+ *	from interrupt state @pri must be %GFP_ATOMIC
+ *
+ *	%NULL is returned on a memory allocation failure.
+ */
+static inline struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
+{
+	if (skb_cloned(skb)) {
+		struct sk_buff *nskb = skb_copy(skb, pri);
+		kfree_skb(skb);	/* Free our shared copy */
+		skb = nskb;
+	}
+	return skb;
+}
+
+/**
+ *	skb_peek
+ *	@list_: list to peek at
+ *
+ *	Peek an &sk_buff. Unlike most other operations you _MUST_
+ *	be careful with this one. A peek leaves the buffer on the
+ *	list and someone else may run off with it. You must hold
+ *	the appropriate locks or have a private queue to do this.
+ *
+ *	Returns %NULL for an empty list or a pointer to the head element.
+ *	The reference count is not incremented and the reference is therefore
+ *	volatile. Use with caution.
+ */
+static inline struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+	struct sk_buff *list = ((struct sk_buff *)list_)->next;
+	if (list == (struct sk_buff *)list_)
+		list = NULL;
+	return list;
+}
+
+/**
+ *	skb_peek_tail
+ *	@list_: list to peek at
+ *
+ *	Peek an &sk_buff. Unlike most other operations you _MUST_
+ *	be careful with this one. A peek leaves the buffer on the
+ *	list and someone else may run off with it. You must hold
+ *	the appropriate locks or have a private queue to do this.
+ *
+ *	Returns %NULL for an empty list or a pointer to the tail element.
+ *	The reference count is not incremented and the reference is therefore
+ *	volatile. Use with caution.
+ */
+static inline struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
+{
+	struct sk_buff *list = ((struct sk_buff *)list_)->prev;
+	if (list == (struct sk_buff *)list_)
+		list = NULL;
+	return list;
+}
+
+/**
+ *	skb_queue_len	- get queue length
+ *	@list_: list to measure
+ *
+ *	Return the length of an &sk_buff queue.
+ */
+static inline __u32 skb_queue_len(struct sk_buff_head *list_)
+{
+	return list_->qlen;
+}
+
+static inline void skb_queue_head_init(struct sk_buff_head *list)
+{
+	spin_lock_init(&list->lock);
+	list->prev = list->next = (struct sk_buff *)list;
+	list->qlen = 0;
+}
+
+/*
+ *	Insert an sk_buff at the start of a list.
+ *
+ *	The "__skb_xxxx()" functions are the non-atomic ones that
+ *	can only be called with interrupts disabled.
+ */
+
+/**
+ *	__skb_queue_head - queue a buffer at the list head
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the start of a list. This function takes no locks
+ *	and you must therefore hold required locks before calling it.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+static inline void __skb_queue_head(struct sk_buff_head *list,
+				    struct sk_buff *newsk)
+{
+	struct sk_buff *prev, *next;
+
+	newsk->list = list;
+	list->qlen++;
+	prev = (struct sk_buff *)list;
+	next = prev->next;
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev  = prev->next = newsk;
+}
+
+
+/**
+ *	skb_queue_head - queue a buffer at the list head
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the start of the list. This function takes the
+ *	list lock and can be used safely with other locking &sk_buff functions
+ *	safely.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+static inline void skb_queue_head(struct sk_buff_head *list,
+				  struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	__skb_queue_head(list, newsk);
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+/**
+ *	__skb_queue_tail - queue a buffer at the list tail
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the end of a list. This function takes no locks
+ *	and you must therefore hold required locks before calling it.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+static inline void __skb_queue_tail(struct sk_buff_head *list,
+				   struct sk_buff *newsk)
+{
+	struct sk_buff *prev, *next;
+
+	newsk->list = list;
+	list->qlen++;
+	next = (struct sk_buff *)list;
+	prev = next->prev;
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev  = prev->next = newsk;
+}
+
+/**
+ *	skb_queue_tail - queue a buffer at the list tail
+ *	@list: list to use
+ *	@newsk: buffer to queue
+ *
+ *	Queue a buffer at the tail of the list. This function takes the
+ *	list lock and can be used safely with other locking &sk_buff functions
+ *	safely.
+ *
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+static inline void skb_queue_tail(struct sk_buff_head *list,
+				  struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->lock, flags);
+	__skb_queue_tail(list, newsk);
+	spin_unlock_irqrestore(&list->lock, flags);
+}
+
+/**
+ *	__skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. This function does not take any locks
+ *	so must be used with appropriate locks held only. The head item is
+ *	returned or %NULL if the list is empty.
+ */
+static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
+{
+	struct sk_buff *next, *prev, *result;
+
+	prev = (struct sk_buff *) list;
+	next = prev->next;
+	result = NULL;
+	if (next != prev) {
+		result	     = next;
+		next	     = next->next;
+		list->qlen--;
+		next->prev   = prev;
+		prev->next   = next;
+		result->next = result->prev = NULL;
+		result->list = NULL;
+	}
+	return result;
+}
+
+/**
+ *	skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. The list lock is taken so the function
+ *	may be used safely with other locking list functions. The head item is
+ *	returned or %NULL if the list is empty.
+ */
+
+static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+{
+	unsigned long flags;
+	struct sk_buff *result;
+
+	spin_lock_irqsave(&list->lock, flags);
+	result = __skb_dequeue(list);
+	spin_unlock_irqrestore(&list->lock, flags);
+	return result;
+}
+
+/*
+ *	Insert a packet on a list.
+ */
+
+static inline void __skb_insert(struct sk_buff *newsk,
+				struct sk_buff *prev, struct sk_buff *next,
+				struct sk_buff_head *list)
+{
+	newsk->next = next;
+	newsk->prev = prev;
+	next->prev  = prev->next = newsk;
+	newsk->list = list;
+	list->qlen++;
+}
+
+/**
+ *	skb_insert	-	insert a buffer
+ *	@old: buffer to insert before
+ *	@newsk: buffer to insert
+ *
+ *	Place a packet before a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+
+static inline void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&old->list->lock, flags);
+	__skb_insert(newsk, old->prev, old, old->list);
+	spin_unlock_irqrestore(&old->list->lock, flags);
+}
+
+/*
+ *	Place a packet after a given packet in a list.
+ */
+
+static inline void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	__skb_insert(newsk, old, old->next, old->list);
+}
+
+/**
+ *	skb_append	-	append a buffer
+ *	@old: buffer to insert after
+ *	@newsk: buffer to insert
+ *
+ *	Place a packet after a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls.
+ *	A buffer cannot be placed on two lists at the same time.
+ */
+
+
+static inline void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&old->list->lock, flags);
+	__skb_append(old, newsk);
+	spin_unlock_irqrestore(&old->list->lock, flags);
+}
+
+/*
+ * remove sk_buff from list. _Must_ be called atomically, and with
+ * the list known..
+ */
+static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+{
+	struct sk_buff *next, *prev;
+
+	list->qlen--;
+	next	   = skb->next;
+	prev	   = skb->prev;
+	skb->next  = skb->prev = NULL;
+	skb->list  = NULL;
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ *	skb_unlink	-	remove a buffer from a list
+ *	@skb: buffer to remove
+ *
+ *	Place a packet after a given packet in a list. The list locks are taken
+ *	and this function is atomic with respect to other list locked calls
+ *
+ *	Works even without knowing the list it is sitting on, which can be
+ *	handy at times. It also means that THE LIST MUST EXIST when you
+ *	unlink. Thus a list must have its contents unlinked before it is
+ *	destroyed.
+ */
+static inline void skb_unlink(struct sk_buff *skb)
+{
+	struct sk_buff_head *list = skb->list;
+
+	if (list) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&list->lock, flags);
+		if (skb->list == list)
+			__skb_unlink(skb, skb->list);
+		spin_unlock_irqrestore(&list->lock, flags);
+	}
+}
+
+/* XXX: more streamlined implementation */
+
+/**
+ *	__skb_dequeue_tail - remove from the tail of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the tail of the list. This function does not take any locks
+ *	so must be used with appropriate locks held only. The tail item is
+ *	returned or %NULL if the list is empty.
+ */
+static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
+{
+	struct sk_buff *skb = skb_peek_tail(list);
+	if (skb)
+		__skb_unlink(skb, list);
+	return skb;
+}
+
+/**
+ *	skb_dequeue - remove from the head of the queue
+ *	@list: list to dequeue from
+ *
+ *	Remove the head of the list. The list lock is taken so the function
+ *	may be used safely with other locking list functions. The tail item is
+ *	returned or %NULL if the list is empty.
+ */
+static inline struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
+{
+	unsigned long flags;
+	struct sk_buff *result;
+
+	spin_lock_irqsave(&list->lock, flags);
+	result = __skb_dequeue_tail(list);
+	spin_unlock_irqrestore(&list->lock, flags);
+	return result;
+}
+
+static inline int skb_is_nonlinear(const struct sk_buff *skb)
+{
+	return skb->data_len;
+}
+
+static inline int skb_headlen(const struct sk_buff *skb)
+{
+	return skb->len - skb->data_len;
+}
+
+static inline int skb_pagelen(const struct sk_buff *skb)
+{
+	int i, len = 0;
+
+	for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--)
+		len += skb_shinfo(skb)->frags[i].size;
+	return len + skb_headlen(skb);
+}
+
+#define SKB_PAGE_ASSERT(skb) do { if (skb_shinfo(skb)->nr_frags) \
+					BUG(); } while (0)
+#define SKB_FRAG_ASSERT(skb) do { if (skb_shinfo(skb)->frag_list) \
+					BUG(); } while (0)
+#define SKB_LINEAR_ASSERT(skb) do { if (skb_is_nonlinear(skb)) \
+					BUG(); } while (0)
+
+/*
+ *	Add data to an sk_buff
+ */
+static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
+{
+	unsigned char *tmp = skb->tail;
+	SKB_LINEAR_ASSERT(skb);
+	skb->tail += len;
+	skb->len  += len;
+	return tmp;
+}
+
+/**
+ *	skb_put - add data to a buffer
+ *	@skb: buffer to use
+ *	@len: amount of data to add
+ *
+ *	This function extends the used data area of the buffer. If this would
+ *	exceed the total buffer size the kernel will panic. A pointer to the
+ *	first byte of the extra data is returned.
+ */
+static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
+{
+	unsigned char *tmp = skb->tail;
+	SKB_LINEAR_ASSERT(skb);
+	skb->tail += len;
+	skb->len  += len;
+	if (skb->tail>skb->end)
+		skb_over_panic(skb, len, current_text_addr());
+	return tmp;
+}
+
+static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
+{
+	skb->data -= len;
+	skb->len  += len;
+	return skb->data;
+}
+
+/**
+ *	skb_push - add data to the start of a buffer
+ *	@skb: buffer to use
+ *	@len: amount of data to add
+ *
+ *	This function extends the used data area of the buffer at the buffer
+ *	start. If this would exceed the total buffer headroom the kernel will
+ *	panic. A pointer to the first byte of the extra data is returned.
+ */
+static inline unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+{
+	skb->data -= len;
+	skb->len  += len;
+	if (skb->data<skb->head)
+		skb_under_panic(skb, len, current_text_addr());
+	return skb->data;
+}
+
+static inline char *__skb_pull(struct sk_buff *skb, unsigned int len)
+{
+	skb->len -= len;
+	if (skb->len < skb->data_len)
+		BUG();
+	return skb->data += len;
+}
+
+/**
+ *	skb_pull - remove data from the start of a buffer
+ *	@skb: buffer to use
+ *	@len: amount of data to remove
+ *
+ *	This function removes data from the start of a buffer, returning
+ *	the memory to the headroom. A pointer to the next data in the buffer
+ *	is returned. Once the data has been pulled future pushes will overwrite
+ *	the old data.
+ */
+static inline unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
+{
+	return (len > skb->len) ? NULL : __skb_pull(skb, len);
+}
+
+extern unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta);
+
+static inline char *__pskb_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len > skb_headlen(skb) &&
+	    !__pskb_pull_tail(skb, len-skb_headlen(skb)))
+		return NULL;
+	skb->len -= len;
+	return skb->data += len;
+}
+
+static inline unsigned char *pskb_pull(struct sk_buff *skb, unsigned int len)
+{
+	return (len > skb->len) ? NULL : __pskb_pull(skb, len);
+}
+
+static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len)
+{
+	if (len <= skb_headlen(skb))
+		return 1;
+	if (len > skb->len)
+		return 0;
+	return __pskb_pull_tail(skb, len-skb_headlen(skb)) != NULL;
+}
+
+/**
+ *	skb_headroom - bytes at buffer head
+ *	@skb: buffer to check
+ *
+ *	Return the number of bytes of free space at the head of an &sk_buff.
+ */
+static inline int skb_headroom(const struct sk_buff *skb)
+{
+	return skb->data - skb->head;
+}
+
+/**
+ *	skb_tailroom - bytes at buffer end
+ *	@skb: buffer to check
+ *
+ *	Return the number of bytes of free space at the tail of an sk_buff
+ */
+static inline int skb_tailroom(const struct sk_buff *skb)
+{
+	return skb_is_nonlinear(skb) ? 0 : skb->end - skb->tail;
+}
+
+/**
+ *	skb_reserve - adjust headroom
+ *	@skb: buffer to alter
+ *	@len: bytes to move
+ *
+ *	Increase the headroom of an empty &sk_buff by reducing the tail
+ *	room. This is only allowed for an empty buffer.
+ */
+static inline void skb_reserve(struct sk_buff *skb, unsigned int len)
+{
+	skb->data += len;
+	skb->tail += len;
+}
+
+extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc);
+
+static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (!skb->data_len) {
+		skb->len  = len;
+		skb->tail = skb->data + len;
+	} else
+		___pskb_trim(skb, len, 0);
+}
+
+/**
+ *	skb_trim - remove end from a buffer
+ *	@skb: buffer to alter
+ *	@len: new length
+ *
+ *	Cut the length of a buffer down by removing data from the tail. If
+ *	the buffer is already under the length specified it is not modified.
+ */
+static inline void skb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (skb->len > len)
+		__skb_trim(skb, len);
+}
+
+
+static inline int __pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	if (!skb->data_len) {
+		skb->len  = len;
+		skb->tail = skb->data+len;
+		return 0;
+	}
+	return ___pskb_trim(skb, len, 1);
+}
+
+static inline int pskb_trim(struct sk_buff *skb, unsigned int len)
+{
+	return (len < skb->len) ? __pskb_trim(skb, len) : 0;
+}
+
+/**
+ *	skb_orphan - orphan a buffer
+ *	@skb: buffer to orphan
+ *
+ *	If a buffer currently has an owner then we call the owner's
+ *	destructor function and make the @skb unowned. The buffer continues
+ *	to exist but is no longer charged to its former owner.
+ */
+static inline void skb_orphan(struct sk_buff *skb)
+{
+	if (skb->destructor)
+		skb->destructor(skb);
+	skb->destructor = NULL;
+	skb->sk		= NULL;
+}
+
+/**
+ *	skb_queue_purge - empty a list
+ *	@list: list to empty
+ *
+ *	Delete all buffers on an &sk_buff list. Each buffer is removed from
+ *	the list and one reference dropped. This function takes the list
+ *	lock and is atomic with respect to other list locking functions.
+ */
+static inline void skb_queue_purge(struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	while ((skb = skb_dequeue(list)) != NULL)
+		kfree_skb(skb);
+}
+
+/**
+ *	__skb_queue_purge - empty a list
+ *	@list: list to empty
+ *
+ *	Delete all buffers on an &sk_buff list. Each buffer is removed from
+ *	the list and one reference dropped. This function does not take the
+ *	list lock and the caller must hold the relevant locks to use it.
+ */
+static inline void __skb_queue_purge(struct sk_buff_head *list)
+{
+	struct sk_buff *skb;
+	while ((skb = __skb_dequeue(list)) != NULL)
+		kfree_skb(skb);
+}
+
+/**
+ *	__dev_alloc_skb - allocate an skbuff for sending
+ *	@length: length to allocate
+ *	@gfp_mask: get_free_pages mask, passed to alloc_skb
+ *
+ *	Allocate a new &sk_buff and assign it a usage count of one. The
+ *	buffer has unspecified headroom built in. Users should allocate
+ *	the headroom they think they need without accounting for the
+ *	built in space. The built in space is used for optimisations.
+ *
+ *	%NULL is returned in there is no free memory.
+ */
+static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
+					      int gfp_mask)
+{
+	struct sk_buff *skb = alloc_skb(length + 16, gfp_mask);
+	if (skb)
+		skb_reserve(skb, 16);
+	return skb;
+}
+
+/**
+ *	dev_alloc_skb - allocate an skbuff for sending
+ *	@length: length to allocate
+ *
+ *	Allocate a new &sk_buff and assign it a usage count of one. The
+ *	buffer has unspecified headroom built in. Users should allocate
+ *	the headroom they think they need without accounting for the
+ *	built in space. The built in space is used for optimisations.
+ *
+ *	%NULL is returned in there is no free memory. Although this function
+ *	allocates memory it can be called from an interrupt.
+ */
+static inline struct sk_buff *dev_alloc_skb(unsigned int length)
+{
+	return __dev_alloc_skb(length, GFP_ATOMIC);
+}
+
+/**
+ *	skb_cow - copy header of skb when it is required
+ *	@skb: buffer to cow
+ *	@headroom: needed headroom
+ *
+ *	If the skb passed lacks sufficient headroom or its data part
+ *	is shared, data is reallocated. If reallocation fails, an error
+ *	is returned and original skb is not changed.
+ *
+ *	The result is skb with writable area skb->head...skb->tail
+ *	and at least @headroom of space at head.
+ */
+static inline int skb_cow(struct sk_buff *skb, unsigned int headroom)
+{
+	int delta = (headroom > 16 ? headroom : 16) - skb_headroom(skb);
+
+	if (delta < 0)
+		delta = 0;
+
+	if (delta || skb_cloned(skb))
+		return pskb_expand_head(skb, (delta + 15) & ~15, 0, GFP_ATOMIC);
+	return 0;
+}
+
+/**
+ *	skb_linearize - convert paged skb to linear one
+ *	@skb: buffer to linarize
+ *	@gfp: allocation mode
+ *
+ *	If there is no free memory -ENOMEM is returned, otherwise zero
+ *	is returned and the old skb data released.
+ */
+int skb_linearize(struct sk_buff *skb, int gfp);
+
+static inline void *kmap_skb_frag(const skb_frag_t *frag)
+{
+#ifdef CONFIG_HIGHMEM
+	if (in_irq())
+		BUG();
+
+	local_bh_disable();
+#endif
+	return kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ);
+}
+
+static inline void kunmap_skb_frag(void *vaddr)
+{
+	kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ);
+#ifdef CONFIG_HIGHMEM
+	local_bh_enable();
+#endif
+}
+
+#define skb_queue_walk(queue, skb) \
+		for (skb = (queue)->next;			\
+		     (skb != (struct sk_buff *)(queue));	\
+		     skb = skb->next)
+
+
+extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
+					 int noblock, int *err);
+extern unsigned int    datagram_poll(struct file *file, struct socket *sock,
+				     struct poll_table_struct *wait);
+extern int	       skb_copy_datagram(const struct sk_buff *from,
+					 int offset, char *to, int size);
+extern int	       skb_copy_datagram_iovec(const struct sk_buff *from,
+					       int offset, struct iovec *to,
+					       int size);
+extern int	       skb_copy_and_csum_datagram(const struct sk_buff *skb,
+						  int offset, u8 *to, int len,
+						  unsigned int *csump);
+extern int	       skb_copy_and_csum_datagram_iovec(const
+							struct sk_buff *skb,
+							int hlen,
+							struct iovec *iov);
+extern void	       skb_free_datagram(struct sock *sk, struct sk_buff *skb);
+extern unsigned int    skb_checksum(const struct sk_buff *skb, int offset,
+				    int len, unsigned int csum);
+extern int	       skb_copy_bits(const struct sk_buff *skb, int offset,
+				     void *to, int len);
+extern unsigned int    skb_copy_and_csum_bits(const struct sk_buff *skb,
+					      int offset, u8 *to, int len,
+					      unsigned int csum);
+extern void	       skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
+
+extern void skb_init(void);
+extern void skb_add_mtu(int mtu);
+
+#ifdef CONFIG_NETFILTER
+static inline void nf_conntrack_put(struct nf_ct_info *nfct)
+{
+	if (nfct && atomic_dec_and_test(&nfct->master->use))
+		nfct->master->destroy(nfct->master);
+}
+static inline void nf_conntrack_get(struct nf_ct_info *nfct)
+{
+	if (nfct)
+		atomic_inc(&nfct->master->use);
+}
+
+static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
+{
+	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
+		kfree(nf_bridge);
+}
+static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
+{
+	if (nf_bridge)
+		atomic_inc(&nf_bridge->use);
+}
+#endif
+
+#endif	/* __KERNEL__ */
+#endif	/* _LINUX_SKBUFF_H */
diff --git a/br-nf-bds/linux2.5/net/bridge/Makefile b/br-nf-bds/linux2.5/net/bridge/Makefile
new file mode 100644
index 0000000..d721b5d
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the IEEE 802.1d ethernet bridging layer.
+#
+
+export-objs := br.o
+
+obj-$(CONFIG_BRIDGE) += bridge.o
+
+bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+			br_stp_if.o br_stp_timer.o
+
+ifeq ($(CONFIG_NETFILTER),y)
+bridge-objs	+= br_netfilter.o
+endif
+
+obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+
+include $(TOPDIR)/Rules.make
diff --git a/br-nf-bds/linux2.5/net/bridge/br.c b/br-nf-bds/linux2.5/net/bridge/br.c
new file mode 100644
index 0000000..e335de2
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/br.c
@@ -0,0 +1,90 @@
+/*
+ *	Generic parts
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br.c,v 1.3 2002/10/21 17:32:51 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/if_bridge.h>
+#include <linux/brlock.h>
+#include <asm/uaccess.h>
+#include "br_private.h"
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "../atm/lec.h"
+#endif
+
+int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+
+void br_dec_use_count()
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void br_inc_use_count()
+{
+	MOD_INC_USE_COUNT;
+}
+
+static int __init br_init(void)
+{
+	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+
+#ifdef CONFIG_NETFILTER
+	if (br_netfilter_init())
+		return 1;
+#endif
+	br_handle_frame_hook = br_handle_frame;
+	br_ioctl_hook = br_ioctl_deviceless_stub;
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = br_fdb_get;
+	br_fdb_put_hook = br_fdb_put;
+#endif
+	register_netdevice_notifier(&br_device_notifier);
+
+	return 0;
+}
+
+static void __br_clear_ioctl_hook(void)
+{
+	br_ioctl_hook = NULL;
+}
+
+static void __exit br_deinit(void)
+{
+#ifdef CONFIG_NETFILTER
+	br_netfilter_fini();
+#endif
+	unregister_netdevice_notifier(&br_device_notifier);
+	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	br_handle_frame_hook = NULL;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = NULL;
+	br_fdb_put_hook = NULL;
+#endif
+}
+
+EXPORT_SYMBOL(br_should_route_hook);
+
+module_init(br_init)
+module_exit(br_deinit)
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/linux2.5/net/bridge/br_forward.c b/br-nf-bds/linux2.5/net/bridge/br_forward.c
new file mode 100644
index 0000000..52c401d
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/br_forward.c
@@ -0,0 +1,156 @@
+/*
+ *	Forwarding decision
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_forward.c,v 1.6 2002/10/21 17:34:03 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
+{
+	if (skb->dev == p->dev ||
+	    p->state != BR_STATE_FORWARDING)
+		return 0;
+
+	return 1;
+}
+
+int br_dev_queue_push_xmit(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER
+	if (skb->nf_bridge)
+		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
+#endif
+	skb_push(skb, ETH_HLEN);
+
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+int br_forward_finish(struct sk_buff *skb)
+{
+	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+			br_dev_queue_push_xmit);
+
+	return 0;
+}
+
+static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	skb->dev = to->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			br_forward_finish);
+}
+
+static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	indev = skb->dev;
+	skb->dev = to->dev;
+
+	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+			br_forward_finish);
+}
+
+/* called under bridge lock */
+void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_deliver(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_forward(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
+	void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
+{
+	struct net_bridge_port *p;
+	struct net_bridge_port *prev;
+
+	if (clone) {
+		struct sk_buff *skb2;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			br->statistics.tx_dropped++;
+			return;
+		}
+
+		skb = skb2;
+	}
+
+	prev = NULL;
+
+	p = br->port_list;
+	while (p != NULL) {
+		if (should_deliver(p, skb)) {
+			if (prev != NULL) {
+				struct sk_buff *skb2;
+
+				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+					br->statistics.tx_dropped++;
+					kfree_skb(skb);
+					return;
+				}
+
+				__packet_hook(prev, skb2);
+			}
+
+			prev = p;
+		}
+
+		p = p->next;
+	}
+
+	if (prev != NULL) {
+		__packet_hook(prev, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_deliver);
+}
+
+/* called under bridge lock */
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_forward);
+}
diff --git a/br-nf-bds/linux2.5/net/bridge/br_input.c b/br-nf-bds/linux2.5/net/bridge/br_input.c
new file mode 100644
index 0000000..4eb67b5
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/br_input.c
@@ -0,0 +1,179 @@
+/*
+ *	Handle incoming frames
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_input.c,v 1.6 2002/10/21 17:34:37 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+static int br_pass_frame_up_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	netif_rx(skb);
+
+	return 0;
+}
+
+static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	br->statistics.rx_packets++;
+	br->statistics.rx_bytes += skb->len;
+
+	indev = skb->dev;
+	skb->dev = &br->dev;
+	skb->pkt_type = PACKET_HOST;
+	skb_push(skb, ETH_HLEN);
+	skb->protocol = eth_type_trans(skb, &br->dev);
+
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
+			br_pass_frame_up_finish);
+}
+
+int br_handle_frame_finish(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_fdb_entry *dst;
+	struct net_bridge_port *p;
+	int passedup;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	passedup = 0;
+	if (br->dev.flags & IFF_PROMISC) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 != NULL) {
+			passedup = 1;
+			br_pass_frame_up(br, skb2);
+		}
+	}
+
+	if (dest[0] & 1) {
+		br_flood_forward(br, skb, !passedup);
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		goto out;
+	}
+
+	dst = br_fdb_get(br, dest);
+	if (dst != NULL && dst->is_local) {
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		else
+			kfree_skb(skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	if (dst != NULL) {
+		br_forward(dst->dst, skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	br_flood_forward(br, skb, 0);
+
+out:
+	read_unlock(&br->lock);
+	return 0;
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+}
+
+int br_handle_frame(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_port *p;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	if (!(br->dev.flags & IFF_UP) ||
+	    p->state == BR_STATE_DISABLED)
+		goto err;
+
+	if (skb->mac.ethernet->h_source[0] & 1)
+		goto err;
+
+	if (p->state == BR_STATE_LEARNING ||
+	    p->state == BR_STATE_FORWARDING)
+		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+	if (br->stp_enabled &&
+	    !memcmp(dest, bridge_ula, 5) &&
+	    !(dest[5] & 0xF0))
+		goto handle_special_frame;
+
+	if (p->state == BR_STATE_FORWARDING) {
+		if (br_should_route_hook && br_should_route_hook(&skb)) {
+			read_unlock(&br->lock);
+			return -1;
+		}
+
+		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+			br_handle_frame_finish);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+
+handle_special_frame:
+	if (!dest[5]) {
+		br_stp_handle_bpdu(skb);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+	kfree_skb(skb);
+	read_unlock(&br->lock);
+	return 0;
+}
diff --git a/br-nf-bds/linux2.5/net/bridge/br_netfilter.c b/br-nf-bds/linux2.5/net/bridge/br_netfilter.c
new file mode 100644
index 0000000..467925d
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/br_netfilter.c
@@ -0,0 +1,618 @@
+/*
+ *	Handle firewalling
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek               <buytenh@gnu.org>
+ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ *
+ *	Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/in_route.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include "br_private.h"
+
+
+#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
+				 (skb->cb))->daddr.ipv4)
+#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
+#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
+				 sizeof(struct bridge_skb_cb)))
+
+#define has_bridge_parent(device)	((device)->br_port != NULL)
+#define bridge_parent(device)		(&((device)->br_port->br->dev))
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it.  Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static struct net_device __fake_net_device = {
+	hard_header_len:	ETH_HLEN
+};
+
+static struct rtable __fake_rtable = {
+	u: {
+		dst: {
+			__refcnt:		ATOMIC_INIT(1),
+			dev:			&__fake_net_device,
+			path:			&__fake_rtable.u.dst,
+			metrics:		{[RTAX_MTU] 1500},
+		}
+	},
+
+	rt_flags:	0
+};
+
+
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static void __br_dnat_complain(void)
+{
+	static unsigned long last_complaint = 0;
+
+	if (jiffies - last_complaint >= 5 * HZ) {
+		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
+			"forwarding to be enabled\n");
+		last_complaint = jiffies;
+	}
+}
+
+
+/* This requires some explaining. If DNAT has taken place,
+ * we will need to fix up the destination Ethernet address,
+ * and this is a tricky process.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ *    port group as it was received on. We can still bridge
+ *    the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ *    a non-bridged device or another bridge port group.
+ *    The packet will need to be routed.
+ *
+ * The correct way of distinguishing between these two cases is to
+ * call ip_route_input() and to look at skb->dst->dev, which is
+ * changed to the destination device if ip_route_input() succeeds.
+ *
+ * Let us first consider the case that ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet
+ * came in on, we can consider this bridging. We then call
+ * skb->dst->output() which will make the packet enter br_nf_local_out()
+ * not much later. In that function it is assured that the iptables
+ * FORWARD chain is traversed for the packet.
+ *
+ * Otherwise, the packet is considered to be routed and we just
+ * change the destination MAC address so that the packet will
+ * later be passed up to the IP stack to be routed.
+ *
+ * Let us now consider the case that ip_route_input() fails:
+ *
+ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
+ * will fail, while __ip_route_output_key() will return success. The source
+ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
+ * thinks we're handling a locally generated packet and won't care
+ * if IP forwarding is allowed. We send a warning message to the users's
+ * log telling her to put IP forwarding on.
+ *
+ * ip_route_input() will also fail if there is no route available.
+ * In that case we just drop the packet.
+ *
+ * --Lennert, 20020411
+ * --Bart, 20020416 (updated)
+ * --Bart, 20021007 (updated)
+ */
+
+static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
+#endif
+
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		skb->pkt_type = PACKET_HOST;
+		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
+	}
+
+	skb->dev = bridge_parent(skb->dev);
+	skb->dst->output(skb);
+	return 0;
+}
+
+static int br_nf_pre_routing_finish(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	struct iphdr *iph = skb->nh.iph;
+	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+#endif
+
+	if (nf_bridge->mask & BRNF_PKT_TYPE) {
+		skb->pkt_type = PACKET_OTHERHOST;
+		nf_bridge->mask ^= BRNF_PKT_TYPE;
+	}
+
+	if (dnat_took_place(skb)) {
+		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
+		    dev)) {
+			struct rtable *rt;
+			struct flowi fl = { .nl_u = 
+			{ .ip4_u = { .daddr = iph->daddr, .saddr = 0 ,
+				     .tos = iph->tos} }, .proto = 0};
+
+			if (!ip_route_output_key(&rt, &fl)) {
+				/* Bridged-and-DNAT'ed traffic doesn't
+				 * require ip_forwarding.
+				 */
+				if (((struct dst_entry *)rt)->dev == dev) {
+					skb->dst = (struct dst_entry *)rt;
+					goto bridged_dnat;
+				}
+				__br_dnat_complain();
+				dst_release((struct dst_entry *)rt);
+			}
+			kfree_skb(skb);
+			return 0;
+		} else {
+			if (skb->dst->dev == dev) {
+bridged_dnat:
+				/* Tell br_nf_local_out this is a
+				 * bridged frame
+				 */
+				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
+				skb->dev = nf_bridge->physindev;
+				clear_cb(skb);
+				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
+					       skb, skb->dev, NULL,
+					       br_nf_pre_routing_finish_bridge,
+					       1);
+				return 0;
+			}
+			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
+			       ETH_ALEN);
+		}
+	} else {
+		skb->dst = (struct dst_entry *)&__fake_rtable;
+		dst_hold(skb->dst);
+	}
+
+	clear_cb(skb);
+	skb->dev = nf_bridge->physindev;
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+		       br_handle_frame_finish, 1);
+
+	return 0;
+}
+
+/* Replicate the checks that IPv4 does on packet reception.
+ * Set skb->dev to the bridge device (i.e. parent of the
+ * receiving device) to make netfilter happy, the REDIRECT
+ * target in particular.  Save the original destination IP
+ * address to be able to detect DNAT afterwards.
+ */
+static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	struct iphdr *iph;
+	__u32 len;
+	struct sk_buff *skb;
+	struct nf_bridge_info *nf_bridge;
+
+	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+		goto out;
+
+	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+		goto inhdr_error;
+
+	iph = skb->nh.iph;
+	if (iph->ihl < 5 || iph->version != 4)
+		goto inhdr_error;
+
+	if (!pskb_may_pull(skb, 4*iph->ihl))
+		goto inhdr_error;
+
+	iph = skb->nh.iph;
+	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+		goto inhdr_error;
+
+	len = ntohs(iph->tot_len);
+	if (skb->len < len || len < 4*iph->ihl)
+		goto inhdr_error;
+
+	if (skb->len > len) {
+		__pskb_trim(skb, len);
+		if (skb->ip_summed == CHECKSUM_HW)
+			skb->ip_summed = CHECKSUM_NONE;
+	}
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+#endif
+ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+		return NF_DROP;
+
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		skb->pkt_type = PACKET_HOST;
+		nf_bridge->mask |= BRNF_PKT_TYPE;
+	}
+
+	nf_bridge->physindev = skb->dev;
+	skb->dev = bridge_parent(skb->dev);
+	store_orig_dstaddr(skb);
+
+	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+		br_nf_pre_routing_finish);
+
+	return NF_STOLEN;
+
+inhdr_error:
+//	IP_INC_STATS_BH(IpInHdrErrors);
+out:
+	return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one.  On the way up, the
+ * packet would pass through PRE_ROUTING again (which already
+ * took place when the packet entered the bridge), but we
+ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
+ * prevent this from happening.
+ */
+static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+		dst_release(skb->dst);
+		skb->dst = NULL;
+	}
+
+	return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf_forward_finish(struct sk_buff *skb)
+{
+	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+	if (nf_bridge->mask & BRNF_PKT_TYPE) {
+		skb->pkt_type = PACKET_OTHERHOST;
+		nf_bridge->mask ^= BRNF_PKT_TYPE;
+	}
+
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
+			skb->dev, br_forward_finish, 1);
+
+	return 0;
+}
+
+/* This is the 'purely bridged' case.  We pass the packet to
+ * netfilter with indev and outdev set to the bridge device,
+ * but we are still able to filter on the 'real' indev/outdev
+ * because another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
+static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+	struct nf_bridge_info *nf_bridge;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+	nf_bridge = skb->nf_bridge;
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		skb->pkt_type = PACKET_HOST;
+		nf_bridge->mask |= BRNF_PKT_TYPE;
+	}
+
+	nf_bridge->physoutdev = skb->dev;
+
+	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
+			bridge_parent(skb->dev), br_nf_forward_finish);
+
+	return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT ***********************************************/
+static int br_nf_local_out_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
+#endif
+
+	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			br_forward_finish, NF_BR_PRI_FIRST + 1);
+
+	return 0;
+}
+
+
+/* This function sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device). It also sees bridged-and-DNAT'ed packets.
+ * For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
+ * functions, and give them back later, when we have determined the real
+ * output device. This is done in here.
+ *
+ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
+ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
+ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
+ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
+ * will be executed.
+ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
+ * this packet before, and so the packet was locally originated. We fake
+ * the PF_INET/LOCAL_OUT hook.
+ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
+ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
+ * even routed packets that didn't arrive on a bridge interface have their
+ * nf_bridge->physindev set.
+ */
+
+static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*_okfn)(struct sk_buff *))
+{
+	int (*okfn)(struct sk_buff *skb);
+	struct net_device *realindev;
+	struct sk_buff *skb = *pskb;
+	struct nf_bridge_info *nf_bridge;
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	/* Sometimes we get packets with NULL ->dst here (for example,
+	 * running a dhcp client daemon triggers this).
+	 */
+	if (skb->dst == NULL)
+		return NF_ACCEPT;
+
+	nf_bridge = skb->nf_bridge;
+	nf_bridge->physoutdev = skb->dev;
+
+	realindev = nf_bridge->physindev;
+
+	/* Bridged, take PF_BRIDGE/FORWARD.
+	 * (see big note in front of br_nf_pre_routing_finish)
+	 */
+	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
+		okfn = br_forward_finish;
+
+		if (nf_bridge->mask & BRNF_PKT_TYPE) {
+			skb->pkt_type = PACKET_OTHERHOST;
+			nf_bridge->mask ^= BRNF_PKT_TYPE;
+		}
+
+		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
+			skb->dev, okfn);
+	} else {
+		okfn = br_nf_local_out_finish;
+		/* IP forwarded traffic has a physindev, locally
+		 * generated traffic hasn't.
+		 */
+		if (realindev != NULL) {
+			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
+			    has_bridge_parent(realindev))
+				realindev = bridge_parent(realindev);
+
+			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
+				       bridge_parent(skb->dev), okfn,
+				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
+		} else {
+#ifdef CONFIG_NETFILTER_DEBUG
+			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+#endif
+
+			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
+				       bridge_parent(skb->dev), okfn,
+				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
+		}
+	}
+
+	return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	struct sk_buff *skb = *pskb;
+	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
+
+	/* Be very paranoid.  */
+	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
+				 "bad mac.raw pointer.");
+		if (skb->dev != NULL) {
+			printk("[%s]", skb->dev->name);
+			if (has_bridge_parent(skb->dev))
+				printk("[%s]", bridge_parent(skb->dev)->name);
+		}
+		printk("\n");
+		return NF_ACCEPT;
+	}
+
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return NF_ACCEPT;
+
+	/* Sometimes we get packets with NULL ->dst here (for example,
+	 * running a dhcp client daemon triggers this).
+	 */
+	if (skb->dst == NULL)
+		return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
+#endif
+
+	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
+	 * about the value of skb->pkt_type.
+	 */
+	if (skb->pkt_type == PACKET_OTHERHOST) {
+		skb->pkt_type = PACKET_HOST;
+		nf_bridge->mask |= BRNF_PKT_TYPE;
+	}
+
+	memcpy(nf_bridge->hh, skb->data - 16, 16);
+
+	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+		bridge_parent(skb->dev), br_dev_queue_push_xmit);
+
+	return NF_STOLEN;
+}
+
+
+/* IPv4/SABOTAGE *****************************************************/
+
+/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
+ * for the second time.
+ */
+static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	if (in->hard_start_xmit == br_dev_xmit &&
+	    okfn != br_nf_pre_routing_finish) {
+		okfn(*pskb);
+		return NF_STOLEN;
+	}
+
+	return NF_ACCEPT;
+}
+
+/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
+ * and PF_INET/POST_ROUTING until we have done the forwarding
+ * decision in the bridge code and have determined skb->physoutdev.
+ */
+static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   int (*okfn)(struct sk_buff *))
+{
+	if (out->hard_start_xmit == br_dev_xmit &&
+	    okfn != br_nf_forward_finish &&
+	    okfn != br_nf_local_out_finish &&
+	    okfn != br_dev_queue_push_xmit) {
+		struct sk_buff *skb = *pskb;
+		struct nf_bridge_info *nf_bridge;
+
+		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
+			return NF_DROP;
+
+		nf_bridge = skb->nf_bridge;
+
+		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
+		 * will need the indev then. For a brouter, the real indev
+		 * can be a bridge port, so we make sure br_nf_local_out()
+		 * doesn't use the bridge parent of the indev by using
+		 * the BRNF_DONT_TAKE_PARENT mask.
+		 */
+		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
+			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
+			nf_bridge->physindev = (struct net_device *)in;
+		}
+		okfn(skb);
+		return NF_STOLEN;
+	}
+
+	return NF_ACCEPT;
+}
+
+/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
+ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
+ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
+ * ip_refrag() can return NF_STOLEN.
+ */
+static struct nf_hook_ops br_nf_ops[] = {
+	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
+	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
+	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
+	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
+	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
+	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
+	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
+};
+
+#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
+
+int br_netfilter_init(void)
+{
+	int i;
+
+	for (i = 0; i < NUMHOOKS; i++) {
+		int ret;
+
+		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
+			continue;
+
+		while (i--)
+			nf_unregister_hook(&br_nf_ops[i]);
+
+		return ret;
+	}
+
+	printk(KERN_NOTICE "Bridge firewalling registered\n");
+
+	return 0;
+}
+
+void br_netfilter_fini(void)
+{
+	int i;
+
+	for (i = NUMHOOKS - 1; i >= 0; i--)
+		nf_unregister_hook(&br_nf_ops[i]);
+}
diff --git a/br-nf-bds/linux2.5/net/bridge/br_private.h b/br-nf-bds/linux2.5/net/bridge/br_private.h
new file mode 100644
index 0000000..949c9c1
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/bridge/br_private.h
@@ -0,0 +1,211 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_private.h,v 1.3 2002/09/18 18:28:48 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _BR_PRIVATE_H
+#define _BR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/if_bridge.h>
+#include "br_private_timer.h"
+
+#define BR_HASH_BITS 8
+#define BR_HASH_SIZE (1 << BR_HASH_BITS)
+
+#define BR_HOLD_TIME (1*HZ)
+
+typedef struct bridge_id bridge_id;
+typedef struct mac_addr mac_addr;
+typedef __u16 port_id;
+
+struct bridge_id
+{
+	unsigned char	prio[2];
+	unsigned char	addr[6];
+};
+
+struct mac_addr
+{
+	unsigned char	addr[6];
+	unsigned char	pad[2];
+};
+
+struct net_bridge_fdb_entry
+{
+	struct net_bridge_fdb_entry	*next_hash;
+	struct net_bridge_fdb_entry	**pprev_hash;
+	atomic_t			use_count;
+	mac_addr			addr;
+	struct net_bridge_port		*dst;
+	unsigned long			ageing_timer;
+	unsigned			is_local:1;
+	unsigned			is_static:1;
+};
+
+struct net_bridge_port
+{
+	struct net_bridge_port		*next;
+	struct net_bridge		*br;
+	struct net_device		*dev;
+	int				port_no;
+
+	/* STP */
+	port_id				port_id;
+	int				state;
+	int				path_cost;
+	bridge_id			designated_root;
+	int				designated_cost;
+	bridge_id			designated_bridge;
+	port_id				designated_port;
+	unsigned			topology_change_ack:1;
+	unsigned			config_pending:1;
+	int				priority;
+
+	struct br_timer			forward_delay_timer;
+	struct br_timer			hold_timer;
+	struct br_timer			message_age_timer;
+};
+
+struct net_bridge
+{
+	struct net_bridge		*next;
+	rwlock_t			lock;
+	struct net_bridge_port		*port_list;
+	struct net_device		dev;
+	struct net_device_stats		statistics;
+	rwlock_t			hash_lock;
+	struct net_bridge_fdb_entry	*hash[BR_HASH_SIZE];
+	struct timer_list		tick;
+
+	/* STP */
+	bridge_id			designated_root;
+	int				root_path_cost;
+	int				root_port;
+	int				max_age;
+	int				hello_time;
+	int				forward_delay;
+	bridge_id			bridge_id;
+	int				bridge_max_age;
+	int				bridge_hello_time;
+	int				bridge_forward_delay;
+	unsigned			stp_enabled:1;
+	unsigned			topology_change:1;
+	unsigned			topology_change_detected:1;
+
+	struct br_timer			hello_timer;
+	struct br_timer			tcn_timer;
+	struct br_timer			topology_change_timer;
+	struct br_timer			gc_timer;
+
+	int				ageing_time;
+	int				gc_interval;
+};
+
+extern struct notifier_block br_device_notifier;
+extern unsigned char bridge_ula[6];
+
+/* br.c */
+extern void br_dec_use_count(void);
+extern void br_inc_use_count(void);
+
+/* br_device.c */
+extern void br_dev_setup(struct net_device *dev);
+extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* br_fdb.c */
+extern void br_fdb_changeaddr(struct net_bridge_port *p,
+		       unsigned char *newaddr);
+extern void br_fdb_cleanup(struct net_bridge *br);
+extern void br_fdb_delete_by_port(struct net_bridge *br,
+			   struct net_bridge_port *p);
+extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
+					unsigned char *addr);
+extern void br_fdb_put(struct net_bridge_fdb_entry *ent);
+extern int  br_fdb_get_entries(struct net_bridge *br,
+			unsigned char *_buf,
+			int maxnum,
+			int offset);
+extern void br_fdb_insert(struct net_bridge *br,
+		   struct net_bridge_port *source,
+		   unsigned char *addr,
+		   int is_local);
+
+/* br_forward.c */
+extern void br_deliver(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+extern void br_forward(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern int br_forward_finish(struct sk_buff *skb);
+extern void br_flood_deliver(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+extern void br_flood_forward(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+
+/* br_if.c */
+extern int br_add_bridge(char *name);
+extern int br_del_bridge(char *name);
+extern int br_add_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_del_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_get_bridge_ifindices(int *indices,
+			    int num);
+extern void br_get_port_ifindices(struct net_bridge *br,
+			   int *ifindices);
+
+/* br_input.c */
+extern int br_handle_frame_finish(struct sk_buff *skb);
+extern int br_handle_frame(struct sk_buff *skb);
+
+/* br_ioctl.c */
+extern void br_call_ioctl_atomic(void (*fn)(void));
+extern int br_ioctl(struct net_bridge *br,
+	     unsigned int cmd,
+	     unsigned long arg0,
+	     unsigned long arg1,
+	     unsigned long arg2);
+extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_netfilter.c */
+extern int br_netfilter_init(void);
+extern void br_netfilter_fini(void);
+
+/* br_stp.c */
+extern int br_is_root_bridge(struct net_bridge *br);
+extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+				    int port_no);
+extern void br_init_port(struct net_bridge_port *p);
+extern port_id br_make_port_id(struct net_bridge_port *p);
+extern void br_become_designated_port(struct net_bridge_port *p);
+
+/* br_stp_if.c */
+extern void br_stp_enable_bridge(struct net_bridge *br);
+extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_enable_port(struct net_bridge_port *p);
+extern void br_stp_disable_port(struct net_bridge_port *p);
+extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
+extern void br_stp_set_bridge_priority(struct net_bridge *br,
+				int newprio);
+extern void br_stp_set_port_priority(struct net_bridge_port *p,
+			      int newprio);
+extern void br_stp_set_path_cost(struct net_bridge_port *p,
+			  int path_cost);
+
+/* br_stp_bpdu.c */
+extern void br_stp_handle_bpdu(struct sk_buff *skb);
+
+#endif
diff --git a/br-nf-bds/linux2.5/net/core/netfilter.c b/br-nf-bds/linux2.5/net/core/netfilter.c
new file mode 100644
index 0000000..f5a5af3
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/core/netfilter.c
@@ -0,0 +1,660 @@
+/* netfilter.c: look after the filters for various protocols. 
+ * Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
+ *
+ * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
+ * way.
+ *
+ * Rusty Russell (C)2000 -- This code is GPL.
+ *
+ * February 2000: Modified by James Morris to have 1 queue per protocol.
+ * 15-Mar-2000:   Added NF_REPEAT --RR.
+ */
+#include <linux/config.h>
+#include <linux/netfilter.h>
+#include <net/protocol.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/brlock.h>
+#include <linux/inetdevice.h>
+#include <net/sock.h>
+#include <net/route.h>
+#include <linux/ip.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* In this code, we can be waiting indefinitely for userspace to
+ * service a packet if a hook returns NF_QUEUE.  We could keep a count
+ * of skbuffs queued for userspace, and not deregister a hook unless
+ * this is zero, but that sucks.  Now, we simply check when the
+ * packets come back: if the hook is gone, the packet is discarded. */
+#ifdef CONFIG_NETFILTER_DEBUG
+#define NFDEBUG(format, args...)  printk(format , ## args)
+#else
+#define NFDEBUG(format, args...)
+#endif
+
+/* Sockopts only registered and called from user context, so
+   BR_NETPROTO_LOCK would be overkill.  Also, [gs]etsockopt calls may
+   sleep. */
+static DECLARE_MUTEX(nf_sockopt_mutex);
+
+struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
+static LIST_HEAD(nf_sockopts);
+
+/* 
+ * A queue handler may be registered for each protocol.  Each is protected by
+ * long term mutex.  The handler must provide an an outfn() to accept packets
+ * for queueing and must reinject all packets it receives, no matter what.
+ */
+static struct nf_queue_handler_t {
+	nf_queue_outfn_t outfn;
+	void *data;
+} queue_handler[NPROTO];
+
+int nf_register_hook(struct nf_hook_ops *reg)
+{
+	struct list_head *i;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	for (i = nf_hooks[reg->pf][reg->hooknum].next; 
+	     i != &nf_hooks[reg->pf][reg->hooknum]; 
+	     i = i->next) {
+		if (reg->priority < ((struct nf_hook_ops *)i)->priority)
+			break;
+	}
+	list_add(&reg->list, i->prev);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	return 0;
+}
+
+void nf_unregister_hook(struct nf_hook_ops *reg)
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	list_del(&reg->list);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+/* Do exclusive ranges overlap? */
+static inline int overlap(int min1, int max1, int min2, int max2)
+{
+	return max1 > min2 && min1 < max2;
+}
+
+/* Functions to register sockopt ranges (exclusive). */
+int nf_register_sockopt(struct nf_sockopt_ops *reg)
+{
+	struct list_head *i;
+	int ret = 0;
+
+	if (down_interruptible(&nf_sockopt_mutex) != 0)
+		return -EINTR;
+
+	for (i = nf_sockopts.next; i != &nf_sockopts; i = i->next) {
+		struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i;
+		if (ops->pf == reg->pf
+		    && (overlap(ops->set_optmin, ops->set_optmax, 
+				reg->set_optmin, reg->set_optmax)
+			|| overlap(ops->get_optmin, ops->get_optmax, 
+				   reg->get_optmin, reg->get_optmax))) {
+			NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
+				ops->set_optmin, ops->set_optmax, 
+				ops->get_optmin, ops->get_optmax, 
+				reg->set_optmin, reg->set_optmax,
+				reg->get_optmin, reg->get_optmax);
+			ret = -EBUSY;
+			goto out;
+		}
+	}
+
+	list_add(&reg->list, &nf_sockopts);
+out:
+	up(&nf_sockopt_mutex);
+	return ret;
+}
+
+void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
+{
+	/* No point being interruptible: we're probably in cleanup_module() */
+ restart:
+	down(&nf_sockopt_mutex);
+	if (reg->use != 0) {
+		/* To be woken by nf_sockopt call... */
+		/* FIXME: Stuart Young's name appears gratuitously. */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		reg->cleanup_task = current;
+		up(&nf_sockopt_mutex);
+		schedule();
+		goto restart;
+	}
+	list_del(&reg->list);
+	up(&nf_sockopt_mutex);
+}
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4.h>
+
+static void debug_print_hooks_ip(unsigned int nf_debug)
+{
+	if (nf_debug & (1 << NF_IP_PRE_ROUTING)) {
+		printk("PRE_ROUTING ");
+		nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+	}
+	if (nf_debug & (1 << NF_IP_LOCAL_IN)) {
+		printk("LOCAL_IN ");
+		nf_debug ^= (1 << NF_IP_LOCAL_IN);
+	}
+	if (nf_debug & (1 << NF_IP_FORWARD)) {
+		printk("FORWARD ");
+		nf_debug ^= (1 << NF_IP_FORWARD);
+	}
+	if (nf_debug & (1 << NF_IP_LOCAL_OUT)) {
+		printk("LOCAL_OUT ");
+		nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+	}
+	if (nf_debug & (1 << NF_IP_POST_ROUTING)) {
+		printk("POST_ROUTING ");
+		nf_debug ^= (1 << NF_IP_POST_ROUTING);
+	}
+	if (nf_debug)
+		printk("Crap bits: 0x%04X", nf_debug);
+	printk("\n");
+}
+
+void nf_dump_skb(int pf, struct sk_buff *skb)
+{
+	printk("skb: pf=%i %s dev=%s len=%u\n", 
+	       pf,
+	       skb->sk ? "(owned)" : "(unowned)",
+	       skb->dev ? skb->dev->name : "(no dev)",
+	       skb->len);
+	switch (pf) {
+	case PF_INET: {
+		const struct iphdr *ip = skb->nh.iph;
+		__u32 *opt = (__u32 *) (ip + 1);
+		int opti;
+		__u16 src_port = 0, dst_port = 0;
+
+		if (ip->protocol == IPPROTO_TCP
+		    || ip->protocol == IPPROTO_UDP) {
+			struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+			src_port = ntohs(tcp->source);
+			dst_port = ntohs(tcp->dest);
+		}
+	
+		printk("PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu"
+		       " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
+		       ip->protocol, NIPQUAD(ip->saddr),
+		       src_port, NIPQUAD(ip->daddr),
+		       dst_port,
+		       ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+		       ntohs(ip->frag_off), ip->ttl);
+
+		for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+			printk(" O=0x%8.8X", *opt++);
+		printk("\n");
+	}
+	}
+}
+
+void nf_debug_ip_local_deliver(struct sk_buff *skb)
+{
+	/* If it's a loopback packet, it must have come through
+	 * NF_IP_LOCAL_OUT, NF_IP_RAW_INPUT, NF_IP_PRE_ROUTING and
+	 * NF_IP_LOCAL_IN.  Otherwise, must have gone through
+	 * NF_IP_RAW_INPUT and NF_IP_PRE_ROUTING.  */
+	if (!skb->dev) {
+		printk("ip_local_deliver: skb->dev is NULL.\n");
+	}
+	else if (strcmp(skb->dev->name, "lo") == 0) {
+		if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				      | (1 << NF_IP_POST_ROUTING)
+				      | (1 << NF_IP_PRE_ROUTING)
+				      | (1 << NF_IP_LOCAL_IN))) {
+			printk("ip_local_deliver: bad loopback skb: ");
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	}
+	else {
+		if (skb->nf_debug != ((1<<NF_IP_PRE_ROUTING)
+				      | (1<<NF_IP_LOCAL_IN))) {
+			printk("ip_local_deliver: bad non-lo skb: ");
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	}
+}
+
+void nf_debug_ip_loopback_xmit(struct sk_buff *newskb)
+{
+	if (newskb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				 | (1 << NF_IP_POST_ROUTING))) {
+		printk("ip_dev_loopback_xmit: bad owned skb = %p: ", 
+		       newskb);
+		debug_print_hooks_ip(newskb->nf_debug);
+		nf_dump_skb(PF_INET, newskb);
+	}
+	/* Clear to avoid confusing input check */
+	newskb->nf_debug = 0;
+}
+
+void nf_debug_ip_finish_output2(struct sk_buff *skb)
+{
+	/* If it's owned, it must have gone through the
+	 * NF_IP_LOCAL_OUT and NF_IP_POST_ROUTING.
+	 * Otherwise, must have gone through
+	 * NF_IP_PRE_ROUTING, NF_IP_FORWARD and NF_IP_POST_ROUTING.
+	 */
+	if (skb->sk) {
+		if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+				      | (1 << NF_IP_POST_ROUTING))) {
+			printk("ip_finish_output: bad owned skb = %p: ", skb);
+			debug_print_hooks_ip(skb->nf_debug);
+			nf_dump_skb(PF_INET, skb);
+		}
+	} else {
+		if (skb->nf_debug != ((1 << NF_IP_PRE_ROUTING)
+				      | (1 << NF_IP_FORWARD)
+				      | (1 << NF_IP_POST_ROUTING))) {
+			/* Fragments, entunnelled packets, TCP RSTs
+                           generated by ipt_REJECT will have no
+                           owners, but still may be local */
+			if (skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+					      | (1 << NF_IP_POST_ROUTING))){
+				printk("ip_finish_output:"
+				       " bad unowned skb = %p: ",skb);
+				debug_print_hooks_ip(skb->nf_debug);
+				nf_dump_skb(PF_INET, skb);
+			}
+		}
+	}
+}
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+/* Call get/setsockopt() */
+static int nf_sockopt(struct sock *sk, int pf, int val, 
+		      char *opt, int *len, int get)
+{
+	struct list_head *i;
+	struct nf_sockopt_ops *ops;
+	int ret;
+
+	if (down_interruptible(&nf_sockopt_mutex) != 0)
+		return -EINTR;
+
+	for (i = nf_sockopts.next; i != &nf_sockopts; i = i->next) {
+		ops = (struct nf_sockopt_ops *)i;
+		if (ops->pf == pf) {
+			if (get) {
+				if (val >= ops->get_optmin
+				    && val < ops->get_optmax) {
+					ops->use++;
+					up(&nf_sockopt_mutex);
+					ret = ops->get(sk, val, opt, len);
+					goto out;
+				}
+			} else {
+				if (val >= ops->set_optmin
+				    && val < ops->set_optmax) {
+					ops->use++;
+					up(&nf_sockopt_mutex);
+					ret = ops->set(sk, val, opt, *len);
+					goto out;
+				}
+			}
+		}
+	}
+	up(&nf_sockopt_mutex);
+	return -ENOPROTOOPT;
+	
+ out:
+	down(&nf_sockopt_mutex);
+	ops->use--;
+	if (ops->cleanup_task)
+		wake_up_process(ops->cleanup_task);
+	up(&nf_sockopt_mutex);
+	return ret;
+}
+
+int nf_setsockopt(struct sock *sk, int pf, int val, char *opt,
+		  int len)
+{
+	return nf_sockopt(sk, pf, val, opt, &len, 0);
+}
+
+int nf_getsockopt(struct sock *sk, int pf, int val, char *opt, int *len)
+{
+	return nf_sockopt(sk, pf, val, opt, len, 1);
+}
+
+static unsigned int nf_iterate(struct list_head *head,
+			       struct sk_buff **skb,
+			       int hook,
+			       const struct net_device *indev,
+			       const struct net_device *outdev,
+			       struct list_head **i,
+			       int (*okfn)(struct sk_buff *),
+			       int hook_thresh)
+{
+	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
+
+		if (hook_thresh > elem->priority)
+			continue;
+
+		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+		case NF_QUEUE:
+			return NF_QUEUE;
+
+		case NF_STOLEN:
+			return NF_STOLEN;
+
+		case NF_DROP:
+			return NF_DROP;
+
+		case NF_REPEAT:
+			*i = (*i)->prev;
+			break;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+		case NF_ACCEPT:
+			break;
+
+		default:
+			NFDEBUG("Evil return from %p(%u).\n", 
+				elem->hook, hook);
+#endif
+		}
+	}
+	return NF_ACCEPT;
+}
+
+int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
+{      
+	int ret;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	if (queue_handler[pf].outfn)
+		ret = -EBUSY;
+	else {
+		queue_handler[pf].outfn = outfn;
+		queue_handler[pf].data = data;
+		ret = 0;
+	}
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+	return ret;
+}
+
+/* The caller must flush their queue before this */
+int nf_unregister_queue_handler(int pf)
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	queue_handler[pf].outfn = NULL;
+	queue_handler[pf].data = NULL;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	return 0;
+}
+
+/* 
+ * Any packet that leaves via this function must come back 
+ * through nf_reinject().
+ */
+static void nf_queue(struct sk_buff *skb, 
+		     struct list_head *elem, 
+		     int pf, unsigned int hook,
+		     struct net_device *indev,
+		     struct net_device *outdev,
+		     int (*okfn)(struct sk_buff *))
+{
+	int status;
+	struct nf_info *info;
+	struct net_device *physindev = NULL;
+	struct net_device *physoutdev = NULL;
+
+	if (!queue_handler[pf].outfn) {
+		kfree_skb(skb);
+		return;
+	}
+
+	info = kmalloc(sizeof(*info), GFP_ATOMIC);
+	if (!info) {
+		if (net_ratelimit())
+			printk(KERN_ERR "OOM queueing packet %p\n",
+			       skb);
+		kfree_skb(skb);
+		return;
+	}
+
+	*info = (struct nf_info) { 
+		(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
+
+	/* Bump dev refs so they don't vanish while packet is out */
+	if (indev) dev_hold(indev);
+	if (outdev) dev_hold(outdev);
+
+	if (skb->nf_bridge) {
+		physindev = skb->nf_bridge->physindev;
+		if (physindev) dev_hold(physindev);
+		physoutdev = skb->nf_bridge->physoutdev;
+		if (physoutdev) dev_hold(physoutdev);
+	}
+
+	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+	if (status < 0) {
+		/* James M doesn't say fuck enough. */
+		if (indev) dev_put(indev);
+		if (outdev) dev_put(outdev);
+		if (physindev) dev_put(physindev);
+		if (physoutdev) dev_put(physoutdev);
+		kfree(info);
+		kfree_skb(skb);
+		return;
+	}
+}
+
+int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+		 struct net_device *indev,
+		 struct net_device *outdev,
+		 int (*okfn)(struct sk_buff *),
+		 int hook_thresh)
+{
+	struct list_head *elem;
+	unsigned int verdict;
+	int ret = 0;
+
+	/* This stopgap cannot be removed until all the hooks are audited. */
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	if (skb->ip_summed == CHECKSUM_HW) {
+		if (outdev == NULL) {
+			skb->ip_summed = CHECKSUM_NONE;
+		} else {
+			skb_checksum_help(skb);
+		}
+	}
+
+	/* We may already have this, but read-locks nest anyway */
+	br_read_lock_bh(BR_NETPROTO_LOCK);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	if (skb->nf_debug & (1 << hook)) {
+		printk("nf_hook: hook %i already set.\n", hook);
+		nf_dump_skb(pf, skb);
+	}
+	skb->nf_debug |= (1 << hook);
+#endif
+
+	elem = &nf_hooks[pf][hook];
+	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+			     outdev, &elem, okfn, hook_thresh);
+	if (verdict == NF_QUEUE) {
+		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+	}
+
+	switch (verdict) {
+	case NF_ACCEPT:
+		ret = okfn(skb);
+		break;
+
+	case NF_DROP:
+		kfree_skb(skb);
+		ret = -EPERM;
+		break;
+	}
+
+	br_read_unlock_bh(BR_NETPROTO_LOCK);
+	return ret;
+}
+
+void nf_reinject(struct sk_buff *skb, struct nf_info *info,
+		 unsigned int verdict)
+{
+	struct list_head *elem = &info->elem->list;
+	struct list_head *i;
+
+	/* We don't have BR_NETPROTO_LOCK here */
+	br_read_lock_bh(BR_NETPROTO_LOCK);
+	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+		if (i == &nf_hooks[info->pf][info->hook]) {
+			/* The module which sent it to userspace is gone. */
+			NFDEBUG("%s: module disappeared, dropping packet.\n",
+			         __FUNCTION__);
+			verdict = NF_DROP;
+			break;
+		}
+	}
+
+	/* Continue traversal iff userspace said ok... */
+	if (verdict == NF_REPEAT) {
+		elem = elem->prev;
+		verdict = NF_ACCEPT;
+	}
+
+	if (verdict == NF_ACCEPT) {
+		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+				     &skb, info->hook, 
+				     info->indev, info->outdev, &elem,
+				     info->okfn, INT_MIN);
+	}
+
+	switch (verdict) {
+	case NF_ACCEPT:
+		info->okfn(skb);
+		break;
+
+	case NF_QUEUE:
+		nf_queue(skb, elem, info->pf, info->hook, 
+			 info->indev, info->outdev, info->okfn);
+		break;
+
+	case NF_DROP:
+		kfree_skb(skb);
+		break;
+	}
+	br_read_unlock_bh(BR_NETPROTO_LOCK);
+
+	/* Release those devices we held, or Alexey will kill me. */
+	if (info->indev) dev_put(info->indev);
+	if (info->outdev) dev_put(info->outdev);
+	
+	kfree(info);
+	return;
+}
+
+#ifdef CONFIG_INET
+/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
+int ip_route_me_harder(struct sk_buff **pskb)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	struct rtable *rt;
+	struct flowi fl = { .nl_u = { .ip4_u =
+				      { .daddr = iph->daddr,
+					.saddr = iph->saddr,
+					.tos = RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+					.fwmark = (*pskb)->nfmark
+#endif
+				      } },
+			    .oif = (*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			    };
+	struct net_device *dev_src = NULL;
+	int err;
+
+	/* accomodate ip_route_output_slow(), which expects the key src to be
+	   0 or a local address; however some non-standard hacks like
+	   ipt_REJECT.c:send_reset() can cause packets with foreign
+           saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */
+	if(fl.fl4_src && !(dev_src = ip_dev_find(fl.fl4_src)))
+		fl.fl4_src = 0;
+
+	if ((err=ip_route_output_key(&rt, &fl)) != 0) {
+		printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n",
+			NIPQUAD(iph->daddr), NIPQUAD(iph->saddr),
+			(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+			(*pskb)->nfmark,
+#else
+			0UL,
+#endif
+			err);
+		goto out;
+	}
+
+	/* Drop old route. */
+	dst_release((*pskb)->dst);
+
+	(*pskb)->dst = &rt->u.dst;
+
+	/* Change in oif may mean change in hh_len. */
+	if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) {
+		struct sk_buff *nskb;
+
+		nskb = skb_realloc_headroom(*pskb,
+					    (*pskb)->dst->dev->hard_header_len);
+		if (!nskb) {
+			err = -ENOMEM;
+			goto out;
+		}
+		if ((*pskb)->sk)
+			skb_set_owner_w(nskb, (*pskb)->sk);
+		kfree_skb(*pskb);
+		*pskb = nskb;
+	}
+
+out:
+	if (dev_src)
+		dev_put(dev_src);
+
+	return err;
+}
+#endif /*CONFIG_INET*/
+
+/* This does not belong here, but ipt_REJECT needs it if connection
+   tracking in use: without this, connection may not be in hash table,
+   and hence manufactured ICMP or RST packets will not be associated
+   with it. */
+void (*ip_ct_attach)(struct sk_buff *, struct nf_ct_info *);
+
+void __init netfilter_init(void)
+{
+	int i, h;
+
+	for (i = 0; i < NPROTO; i++) {
+		for (h = 0; h < NF_MAX_HOOKS; h++)
+			INIT_LIST_HEAD(&nf_hooks[i][h]);
+	}
+}
diff --git a/br-nf-bds/linux2.5/net/core/skbuff.c b/br-nf-bds/linux2.5/net/core/skbuff.c
new file mode 100644
index 0000000..fb9fdf8
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/core/skbuff.c
@@ -0,0 +1,1219 @@
+/*
+ *	Routines having to do with the 'struct sk_buff' memory handlers.
+ *
+ *	Authors:	Alan Cox <iiitac@pyr.swan.ac.uk>
+ *			Florian La Roche <rzsfl@rz.uni-sb.de>
+ *
+ *	Version:	$Id: skbuff.c,v 1.5 2002/10/19 14:30:25 bdschuym Exp $
+ *
+ *	Fixes:
+ *		Alan Cox	:	Fixed the worst of the load
+ *					balancer bugs.
+ *		Dave Platt	:	Interrupt stacking fix.
+ *	Richard Kooijman	:	Timestamp fixes.
+ *		Alan Cox	:	Changed buffer format.
+ *		Alan Cox	:	destructor hook for AF_UNIX etc.
+ *		Linus Torvalds	:	Better skb_clone.
+ *		Alan Cox	:	Added skb_copy.
+ *		Alan Cox	:	Added all the changed routines Linus
+ *					only put in the headers
+ *		Ray VanTassle	:	Fixed --skb->lock in free
+ *		Alan Cox	:	skb_copy copy arp field
+ *		Andi Kleen	:	slabified it.
+ *
+ *	NOTE:
+ *		The __skb_ routines should be called with interrupts
+ *	disabled, or you better be *real* sure that the operation is atomic
+ *	with respect to whatever list is being frobbed (e.g. via lock_sock()
+ *	or via disabling bottom half handlers, etc).
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+/*
+ *	The functions in this file will not compile correctly with gcc 2.4.x
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/cache.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+#include <net/protocol.h>
+#include <net/dst.h>
+#include <net/sock.h>
+#include <net/checksum.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+int sysctl_hot_list_len = 128;
+
+static kmem_cache_t *skbuff_head_cache;
+
+static union {
+	struct sk_buff_head	list;
+	char			pad[SMP_CACHE_BYTES];
+} skb_head_pool[NR_CPUS];
+
+/*
+ *	Keep out-of-line to prevent kernel bloat.
+ *	__builtin_return_address is not used because it is not always
+ *	reliable.
+ */
+
+/**
+ *	skb_over_panic	- 	private function
+ *	@skb: buffer
+ *	@sz: size
+ *	@here: address
+ *
+ *	Out of line support code for skb_put(). Not user callable.
+ */
+void skb_over_panic(struct sk_buff *skb, int sz, void *here)
+{
+	printk(KERN_INFO "skput:over: %p:%d put:%d dev:%s",
+		here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+	BUG();
+}
+
+/**
+ *	skb_under_panic	- 	private function
+ *	@skb: buffer
+ *	@sz: size
+ *	@here: address
+ *
+ *	Out of line support code for skb_push(). Not user callable.
+ */
+
+void skb_under_panic(struct sk_buff *skb, int sz, void *here)
+{
+	printk(KERN_INFO "skput:under: %p:%d put:%d dev:%s",
+               here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+	BUG();
+}
+
+static __inline__ struct sk_buff *skb_head_from_pool(void)
+{
+	struct sk_buff_head *list;
+	struct sk_buff *skb = NULL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	list = &skb_head_pool[smp_processor_id()].list;
+
+	if (skb_queue_len(list))
+		skb = __skb_dequeue(list);
+
+	local_irq_restore(flags);
+	return skb;
+}
+
+static __inline__ void skb_head_to_pool(struct sk_buff *skb)
+{
+	struct sk_buff_head *list;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	list = &skb_head_pool[smp_processor_id()].list;
+
+	if (skb_queue_len(list) < sysctl_hot_list_len) {
+		__skb_queue_head(list, skb);
+		local_irq_restore(flags);
+
+		return;
+	}
+
+	local_irq_restore(flags);
+	kmem_cache_free(skbuff_head_cache, skb);
+}
+
+
+/* 	Allocate a new skbuff. We do this ourselves so we can fill in a few
+ *	'private' fields and also do memory statistics to find all the
+ *	[BEEP] leaks.
+ *
+ */
+
+/**
+ *	alloc_skb	-	allocate a network buffer
+ *	@size: size to allocate
+ *	@gfp_mask: allocation mask
+ *
+ *	Allocate a new &sk_buff. The returned buffer has no headroom and a
+ *	tail room of size bytes. The object has a reference count of one.
+ *	The return is the buffer. On a failure the return is %NULL.
+ *
+ *	Buffers may only be allocated from interrupts using a @gfp_mask of
+ *	%GFP_ATOMIC.
+ */
+struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
+{
+	struct sk_buff *skb;
+	u8 *data;
+
+	if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
+		static int count;
+		if (++count < 5) {
+			printk(KERN_ERR "alloc_skb called nonatomically "
+			       "from interrupt %p\n", NET_CALLER(size));
+ 			BUG();
+		}
+		gfp_mask &= ~__GFP_WAIT;
+	}
+
+	/* Get the HEAD */
+	skb = skb_head_from_pool();
+	if (!skb) {
+		skb = kmem_cache_alloc(skbuff_head_cache,
+				       gfp_mask & ~__GFP_DMA);
+		if (!skb)
+			goto out;
+	}
+
+	/* Get the DATA. Size must match skb_add_mtu(). */
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (!data)
+		goto nodata;
+
+	/* XXX: does not include slab overhead */
+	skb->truesize = size + sizeof(struct sk_buff);
+
+	/* Load the data pointers. */
+	skb->head = skb->data = skb->tail = data;
+	skb->end  = data + size;
+
+	/* Set up other state */
+	skb->len      = 0;
+	skb->cloned   = 0;
+	skb->data_len = 0;
+
+	atomic_set(&skb->users, 1);
+	atomic_set(&(skb_shinfo(skb)->dataref), 1);
+	skb_shinfo(skb)->nr_frags  = 0;
+	skb_shinfo(skb)->tso_size = 0;
+	skb_shinfo(skb)->tso_segs = 0;
+	skb_shinfo(skb)->frag_list = NULL;
+out:
+	return skb;
+nodata:
+	skb_head_to_pool(skb);
+	skb = NULL;
+	goto out;
+}
+
+
+/*
+ *	Slab constructor for a skb head.
+ */
+static inline void skb_headerinit(void *p, kmem_cache_t *cache,
+				  unsigned long flags)
+{
+	struct sk_buff *skb = p;
+
+	skb->next	  = skb->prev = NULL;
+	skb->list	  = NULL;
+	skb->sk		  = NULL;
+	skb->stamp.tv_sec = 0;	/* No idea about time */
+	skb->dev	  = NULL;
+	skb->dst	  = NULL;
+	memset(skb->cb, 0, sizeof(skb->cb));
+	skb->pkt_type	  = PACKET_HOST;	/* Default type */
+	skb->ip_summed	  = 0;
+	skb->priority	  = 0;
+	skb->security	  = 0;	/* By default packets are insecure */
+	skb->destructor	  = NULL;
+
+#ifdef CONFIG_NETFILTER
+	skb->nfmark	  = skb->nfcache = 0;
+	skb->nfct	  = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug	  = 0;
+#endif
+	skb->nf_bridge	  = NULL;
+#endif
+#ifdef CONFIG_NET_SCHED
+	skb->tc_index	  = 0;
+#endif
+}
+
+static void skb_drop_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+	skb_shinfo(skb)->frag_list = NULL;
+
+	do {
+		struct sk_buff *this = list;
+		list = list->next;
+		kfree_skb(this);
+	} while (list);
+}
+
+static void skb_clone_fraglist(struct sk_buff *skb)
+{
+	struct sk_buff *list;
+
+	for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
+		skb_get(list);
+}
+
+static void skb_release_data(struct sk_buff *skb)
+{
+	if (!skb->cloned ||
+	    atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) {
+		if (skb_shinfo(skb)->nr_frags) {
+			int i;
+			for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+				put_page(skb_shinfo(skb)->frags[i].page);
+		}
+
+		if (skb_shinfo(skb)->frag_list)
+			skb_drop_fraglist(skb);
+
+		kfree(skb->head);
+	}
+}
+
+/*
+ *	Free an skbuff by memory without cleaning the state.
+ */
+void kfree_skbmem(struct sk_buff *skb)
+{
+	skb_release_data(skb);
+	skb_head_to_pool(skb);
+}
+
+/**
+ *	__kfree_skb - private function
+ *	@skb: buffer
+ *
+ *	Free an sk_buff. Release anything attached to the buffer.
+ *	Clean the state. This is an internal helper function. Users should
+ *	always call kfree_skb
+ */
+
+void __kfree_skb(struct sk_buff *skb)
+{
+	if (skb->list) {
+	 	printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
+		       "on a list (from %p).\n", NET_CALLER(skb));
+		BUG();
+	}
+
+	dst_release(skb->dst);
+	if(skb->destructor) {
+		if (in_irq())
+			printk(KERN_WARNING "Warning: kfree_skb on "
+					    "hard IRQ %p\n", NET_CALLER(skb));
+		skb->destructor(skb);
+	}
+#ifdef CONFIG_NETFILTER
+	nf_conntrack_put(skb->nfct);
+	nf_bridge_put(skb->nf_bridge);
+#endif
+	skb_headerinit(skb, NULL, 0);  /* clean state */
+	kfree_skbmem(skb);
+}
+
+/**
+ *	skb_clone	-	duplicate an sk_buff
+ *	@skb: buffer to clone
+ *	@gfp_mask: allocation priority
+ *
+ *	Duplicate an &sk_buff. The new one is not owned by a socket. Both
+ *	copies share the same packet data but not structure. The new
+ *	buffer has a reference count of 1. If the allocation fails the
+ *	function returns %NULL otherwise the new buffer is returned.
+ *
+ *	If this function is called from an interrupt gfp_mask() must be
+ *	%GFP_ATOMIC.
+ */
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
+{
+	struct sk_buff *n = skb_head_from_pool();
+
+	if (!n) {
+		n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+		if (!n)
+			return NULL;
+	}
+
+#define C(x) n->x = skb->x
+
+	n->next = n->prev = NULL;
+	n->list = NULL;
+	n->sk = NULL;
+	C(stamp);
+	C(dev);
+	C(h);
+	C(nh);
+	C(mac);
+	C(dst);
+	dst_clone(n->dst);
+	memcpy(n->cb, skb->cb, sizeof(skb->cb));
+	C(len);
+	C(data_len);
+	C(csum);
+	n->cloned = 1;
+	C(pkt_type);
+	C(ip_summed);
+	C(priority);
+	atomic_set(&n->users, 1);
+	C(protocol);
+	C(security);
+	C(truesize);
+	C(head);
+	C(data);
+	C(tail);
+	C(end);
+	n->destructor = NULL;
+#ifdef CONFIG_NETFILTER
+	C(nfmark);
+	C(nfcache);
+	C(nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+	C(nf_debug);
+#endif
+	C(nf_bridge);
+#endif /*CONFIG_NETFILTER*/
+#if defined(CONFIG_HIPPI)
+	C(private);
+#endif
+#ifdef CONFIG_NET_SCHED
+	C(tc_index);
+#endif
+
+	atomic_inc(&(skb_shinfo(skb)->dataref));
+	skb->cloned = 1;
+#ifdef CONFIG_NETFILTER
+	nf_conntrack_get(skb->nfct);
+	nf_bridge_get(skb->nf_bridge);
+#endif
+	return n;
+}
+
+static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	unsigned long offset = new->data - old->data;
+
+	new->list	= NULL;
+	new->sk		= NULL;
+	new->dev	= old->dev;
+	new->priority	= old->priority;
+	new->protocol	= old->protocol;
+	new->dst	= dst_clone(old->dst);
+	new->h.raw	= old->h.raw + offset;
+	new->nh.raw	= old->nh.raw + offset;
+	new->mac.raw	= old->mac.raw + offset;
+	memcpy(new->cb, old->cb, sizeof(old->cb));
+	atomic_set(&new->users, 1);
+	new->pkt_type	= old->pkt_type;
+	new->stamp	= old->stamp;
+	new->destructor = NULL;
+	new->security	= old->security;
+#ifdef CONFIG_NETFILTER
+	new->nfmark	= old->nfmark;
+	new->nfcache	= old->nfcache;
+	new->nfct	= old->nfct;
+	nf_conntrack_get(new->nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+	new->nf_debug	= old->nf_debug;
+#endif
+	new->nf_bridge	= old->nf_bridge;
+	nf_bridge_get(new->nf_bridge);
+#endif
+#ifdef CONFIG_NET_SCHED
+	new->tc_index	= old->tc_index;
+#endif
+}
+
+/**
+ *	skb_copy	-	create private copy of an sk_buff
+ *	@skb: buffer to copy
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and its data. This is used when the
+ *	caller wishes to modify the data and needs a private copy of the
+ *	data to alter. Returns %NULL on failure or the pointer to the buffer
+ *	on success. The returned buffer has a reference count of 1.
+ *
+ *	As by-product this function converts non-linear &sk_buff to linear
+ *	one, so that &sk_buff becomes completely private and caller is allowed
+ *	to modify all the data of returned buffer. This means that this
+ *	function is not recommended for use in circumstances when only
+ *	header is going to be modified. Use pskb_copy() instead.
+ */
+
+struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
+{
+	int headerlen = skb->data - skb->head;
+	/*
+	 *	Allocate the copy buffer
+	 */
+	struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len,
+				      gfp_mask);
+	if (!n)
+		return NULL;
+
+	/* Set the data pointer */
+	skb_reserve(n, headerlen);
+	/* Set the tail pointer and length */
+	skb_put(n, skb->len);
+	n->csum	     = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
+		BUG();
+
+	copy_skb_header(n, skb);
+	return n;
+}
+
+/* Keep head the same: replace data */
+int skb_linearize(struct sk_buff *skb, int gfp_mask)
+{
+	unsigned int size;
+	u8 *data;
+	long offset;
+	struct skb_shared_info *ninfo;
+	int headerlen = skb->data - skb->head;
+	int expand = (skb->tail + skb->data_len) - skb->end;
+
+	if (skb_shared(skb))
+		BUG();
+
+	if (expand <= 0)
+		expand = 0;
+
+	size = skb->end - skb->head + expand;
+	size = SKB_DATA_ALIGN(size);
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (!data)
+		return -ENOMEM;
+
+	/* Copy entire thing */
+	if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))
+		BUG();
+
+	/* Set up shinfo */
+	ninfo = (struct skb_shared_info*)(data + size);
+	atomic_set(&ninfo->dataref, 1);
+	ninfo->tso_size = skb_shinfo(skb)->tso_size;
+	ninfo->tso_segs = skb_shinfo(skb)->tso_segs;
+	ninfo->nr_frags = 0;
+	ninfo->frag_list = NULL;
+
+	/* Offset between the two in bytes */
+	offset = data - skb->head;
+
+	/* Free old data. */
+	skb_release_data(skb);
+
+	skb->head = data;
+	skb->end  = data + size;
+
+	/* Set up new pointers */
+	skb->h.raw   += offset;
+	skb->nh.raw  += offset;
+	skb->mac.raw += offset;
+	skb->tail    += offset;
+	skb->data    += offset;
+
+	/* We are no longer a clone, even if we were. */
+	skb->cloned    = 0;
+
+	skb->tail     += skb->data_len;
+	skb->data_len  = 0;
+	return 0;
+}
+
+
+/**
+ *	pskb_copy	-	create copy of an sk_buff with private head.
+ *	@skb: buffer to copy
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and part of its data, located
+ *	in header. Fragmented data remain shared. This is used when
+ *	the caller wishes to modify only header of &sk_buff and needs
+ *	private copy of the header to alter. Returns %NULL on failure
+ *	or the pointer to the buffer on success.
+ *	The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)
+{
+	/*
+	 *	Allocate the copy buffer
+	 */
+	struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);
+
+	if (!n)
+		goto out;
+
+	/* Set the data pointer */
+	skb_reserve(n, skb->data - skb->head);
+	/* Set the tail pointer and length */
+	skb_put(n, skb_headlen(skb));
+	/* Copy the bytes */
+	memcpy(n->data, skb->data, n->len);
+	n->csum	     = skb->csum;
+	n->ip_summed = skb->ip_summed;
+
+	n->data_len  = skb->data_len;
+	n->len	     = skb->len;
+
+	if (skb_shinfo(skb)->nr_frags) {
+		int i;
+
+		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+			skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+			get_page(skb_shinfo(n)->frags[i].page);
+		}
+		skb_shinfo(n)->nr_frags = i;
+	}
+	skb_shinfo(n)->tso_size = skb_shinfo(skb)->tso_size;
+	skb_shinfo(n)->tso_segs = skb_shinfo(skb)->tso_segs;
+
+	if (skb_shinfo(skb)->frag_list) {
+		skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list;
+		skb_clone_fraglist(n);
+	}
+
+	copy_skb_header(n, skb);
+out:
+	return n;
+}
+
+/**
+ *	pskb_expand_head - reallocate header of &sk_buff
+ *	@skb: buffer to reallocate
+ *	@nhead: room to add at head
+ *	@ntail: room to add at tail
+ *	@gfp_mask: allocation priority
+ *
+ *	Expands (or creates identical copy, if &nhead and &ntail are zero)
+ *	header of skb. &sk_buff itself is not changed. &sk_buff MUST have
+ *	reference count of 1. Returns zero in the case of success or error,
+ *	if expansion failed. In the last case, &sk_buff is not changed.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, int gfp_mask)
+{
+	int i;
+	u8 *data;
+	int size = nhead + (skb->end - skb->head) + ntail;
+	long off;
+
+	if (skb_shared(skb))
+		BUG();
+
+	size = SKB_DATA_ALIGN(size);
+
+	data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
+	if (!data)
+		goto nodata;
+
+	/* Copy only real data... and, alas, header. This should be
+	 * optimized for the cases when header is void. */
+	memcpy(data + nhead, skb->head, skb->tail - skb->head);
+	memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+		get_page(skb_shinfo(skb)->frags[i].page);
+
+	if (skb_shinfo(skb)->frag_list)
+		skb_clone_fraglist(skb);
+
+	skb_release_data(skb);
+
+	off = (data + nhead) - skb->head;
+
+	skb->head     = data;
+	skb->end      = data + size;
+	skb->data    += off;
+	skb->tail    += off;
+	skb->mac.raw += off;
+	skb->h.raw   += off;
+	skb->nh.raw  += off;
+	skb->cloned   = 0;
+	atomic_set(&skb_shinfo(skb)->dataref, 1);
+	return 0;
+
+nodata:
+	return -ENOMEM;
+}
+
+/* Make private copy of skb with writable head and some headroom */
+
+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
+{
+	struct sk_buff *skb2;
+	int delta = headroom - skb_headroom(skb);
+
+	if (delta <= 0)
+		skb2 = pskb_copy(skb, GFP_ATOMIC);
+	else {
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 && pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0,
+					     GFP_ATOMIC)) {
+			kfree_skb(skb2);
+			skb2 = NULL;
+		}
+	}
+	return skb2;
+}
+
+
+/**
+ *	skb_copy_expand	-	copy and expand sk_buff
+ *	@skb: buffer to copy
+ *	@newheadroom: new free bytes at head
+ *	@newtailroom: new free bytes at tail
+ *	@gfp_mask: allocation priority
+ *
+ *	Make a copy of both an &sk_buff and its data and while doing so
+ *	allocate additional space.
+ *
+ *	This is used when the caller wishes to modify the data and needs a
+ *	private copy of the data to alter as well as more space for new fields.
+ *	Returns %NULL on failure or the pointer to the buffer
+ *	on success. The returned buffer has a reference count of 1.
+ *
+ *	You must pass %GFP_ATOMIC as the allocation priority if this function
+ *	is called from an interrupt.
+ *
+ *	BUG ALERT: ip_summed is not copied. Why does this work? Is it used
+ *	only by netfilter in the cases when checksum is recalculated? --ANK
+ */
+struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
+				int newheadroom, int newtailroom, int gfp_mask)
+{
+	/*
+	 *	Allocate the copy buffer
+	 */
+	struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
+				      gfp_mask);
+	if (!n)
+		return NULL;
+
+	skb_reserve(n, newheadroom);
+
+	/* Set the tail pointer and length */
+	skb_put(n, skb->len);
+
+	/* Copy the data only. */
+	if (skb_copy_bits(skb, 0, n->data, skb->len))
+		BUG();
+
+	copy_skb_header(n, skb);
+	skb_shinfo(n)->tso_size = skb_shinfo(skb)->tso_size;
+	skb_shinfo(n)->tso_segs = skb_shinfo(skb)->tso_segs;
+
+	return n;
+}
+
+/* Trims skb to length len. It can change skb pointers, if "realloc" is 1.
+ * If realloc==0 and trimming is impossible without change of data,
+ * it is BUG().
+ */
+
+int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc)
+{
+	int offset = skb_headlen(skb);
+	int nfrags = skb_shinfo(skb)->nr_frags;
+	int i;
+
+	for (i = 0; i < nfrags; i++) {
+		int end = offset + skb_shinfo(skb)->frags[i].size;
+		if (end > len) {
+			if (skb_cloned(skb)) {
+				if (!realloc)
+					BUG();
+				if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+					return -ENOMEM;
+			}
+			if (len <= offset) {
+				put_page(skb_shinfo(skb)->frags[i].page);
+				skb_shinfo(skb)->nr_frags--;
+			} else {
+				skb_shinfo(skb)->frags[i].size = len - offset;
+			}
+		}
+		offset = end;
+	}
+
+	if (offset < len) {
+		skb->data_len -= skb->len - len;
+		skb->len       = len;
+	} else {
+		if (len <= skb_headlen(skb)) {
+			skb->len      = len;
+			skb->data_len = 0;
+			skb->tail     = skb->data + len;
+			if (skb_shinfo(skb)->frag_list && !skb_cloned(skb))
+				skb_drop_fraglist(skb);
+		} else {
+			skb->data_len -= skb->len - len;
+			skb->len       = len;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *	__pskb_pull_tail - advance tail of skb header
+ *	@skb: buffer to reallocate
+ *	@delta: number of bytes to advance tail
+ *
+ *	The function makes a sense only on a fragmented &sk_buff,
+ *	it expands header moving its tail forward and copying necessary
+ *	data from fragmented part.
+ *
+ *	&sk_buff MUST have reference count of 1.
+ *
+ *	Returns %NULL (and &sk_buff does not change) if pull failed
+ *	or value of new tail of skb in the case of success.
+ *
+ *	All the pointers pointing into skb header may change and must be
+ *	reloaded after call to this function.
+ */
+
+/* Moves tail of skb head forward, copying data from fragmented part,
+ * when it is necessary.
+ * 1. It may fail due to malloc failure.
+ * 2. It may change skb pointers.
+ *
+ * It is pretty complicated. Luckily, it is called only in exceptional cases.
+ */
+unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta)
+{
+	/* If skb has not enough free space at tail, get new one
+	 * plus 128 bytes for future expansions. If we have enough
+	 * room at tail, reallocate without expansion only if skb is cloned.
+	 */
+	int i, k, eat = (skb->tail + delta) - skb->end;
+
+	if (eat > 0 || skb_cloned(skb)) {
+		if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,
+				     GFP_ATOMIC))
+			return NULL;
+	}
+
+	if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
+		BUG();
+
+	/* Optimization: no fragments, no reasons to preestimate
+	 * size of pulled pages. Superb.
+	 */
+	if (!skb_shinfo(skb)->frag_list)
+		goto pull_pages;
+
+	/* Estimate size of pulled pages. */
+	eat = delta;
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size >= eat)
+			goto pull_pages;
+		eat -= skb_shinfo(skb)->frags[i].size;
+	}
+
+	/* If we need update frag list, we are in troubles.
+	 * Certainly, it possible to add an offset to skb data,
+	 * but taking into account that pulling is expected to
+	 * be very rare operation, it is worth to fight against
+	 * further bloating skb head and crucify ourselves here instead.
+	 * Pure masohism, indeed. 8)8)
+	 */
+	if (eat) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+		struct sk_buff *clone = NULL;
+		struct sk_buff *insp = NULL;
+
+		do {
+			if (!list)
+				BUG();
+
+			if (list->len <= eat) {
+				/* Eaten as whole. */
+				eat -= list->len;
+				list = list->next;
+				insp = list;
+			} else {
+				/* Eaten partially. */
+
+				if (skb_shared(list)) {
+					/* Sucks! We need to fork list. :-( */
+					clone = skb_clone(list, GFP_ATOMIC);
+					if (!clone)
+						return NULL;
+					insp = list->next;
+					list = clone;
+				} else {
+					/* This may be pulled without
+					 * problems. */
+					insp = list;
+				}
+				if (!pskb_pull(list, eat)) {
+					if (clone)
+						kfree_skb(clone);
+					return NULL;
+				}
+				break;
+			}
+		} while (eat);
+
+		/* Free pulled out fragments. */
+		while ((list = skb_shinfo(skb)->frag_list) != insp) {
+			skb_shinfo(skb)->frag_list = list->next;
+			kfree_skb(list);
+		}
+		/* And insert new clone at head. */
+		if (clone) {
+			clone->next = list;
+			skb_shinfo(skb)->frag_list = clone;
+		}
+	}
+	/* Success! Now we may commit changes to skb data. */
+
+pull_pages:
+	eat = delta;
+	k = 0;
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		if (skb_shinfo(skb)->frags[i].size <= eat) {
+			put_page(skb_shinfo(skb)->frags[i].page);
+			eat -= skb_shinfo(skb)->frags[i].size;
+		} else {
+			skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+			if (eat) {
+				skb_shinfo(skb)->frags[k].page_offset += eat;
+				skb_shinfo(skb)->frags[k].size -= eat;
+				eat = 0;
+			}
+			k++;
+		}
+	}
+	skb_shinfo(skb)->nr_frags = k;
+
+	skb->tail     += delta;
+	skb->data_len -= delta;
+
+	return skb->tail;
+}
+
+/* Copy some data bits from skb to kernel buffer. */
+
+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
+{
+	int i, copy;
+	int start = skb->len - skb->data_len;
+
+	if (offset > (int)skb->len - len)
+		goto fault;
+
+	/* Copy header. */
+	if ((copy = start - offset) > 0) {
+		if (copy > len)
+			copy = len;
+		memcpy(to, skb->data + offset, copy);
+		if ((len -= copy) == 0)
+			return 0;
+		offset += copy;
+		to     += copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset + len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end - offset) > 0) {
+			u8 *vaddr;
+
+			if (copy > len)
+				copy = len;
+
+			vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+			memcpy(to,
+			       vaddr + skb_shinfo(skb)->frags[i].page_offset+
+			       offset - start, copy);
+			kunmap_skb_frag(vaddr);
+
+			if ((len -= copy) == 0)
+				return 0;
+			offset += copy;
+			to     += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+		for (; list; list = list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset + len);
+
+			end = start + list->len;
+			if ((copy = end - offset) > 0) {
+				if (copy > len)
+					copy = len;
+				if (skb_copy_bits(list, offset - start,
+						  to, copy))
+					goto fault;
+				if ((len -= copy) == 0)
+					return 0;
+				offset += copy;
+				to     += copy;
+			}
+			start = end;
+		}
+	}
+	if (!len)
+		return 0;
+
+fault:
+	return -EFAULT;
+}
+
+/* Checksum skb data. */
+
+unsigned int skb_checksum(const struct sk_buff *skb, int offset,
+			  int len, unsigned int csum)
+{
+	int start = skb->len - skb->data_len;
+	int i, copy = start - offset;
+	int pos = 0;
+
+	/* Checksum header. */
+	if (copy > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial(skb->data + offset, copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		pos	= copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset + len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end - offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial(vaddr + frag->page_offset +
+					     offset - start, copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			pos    += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+		for (; list; list = list->next) {
+			int end;
+
+			BUG_TRAP(start <= offset + len);
+
+			end = start + list->len;
+			if ((copy = end - offset) > 0) {
+				unsigned int csum2;
+				if (copy > len)
+					copy = len;
+				csum2 = skb_checksum(list, offset - start,
+						     copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				pos    += copy;
+			}
+			start = end;
+		}
+	}
+	if (len)
+		BUG();
+
+	return csum;
+}
+
+/* Both of above in one bottle. */
+
+unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
+				    u8 *to, int len, unsigned int csum)
+{
+	int start = skb->len - skb->data_len;
+	int i, copy = start - offset;
+	int pos = 0;
+
+	/* Copy header. */
+	if (copy > 0) {
+		if (copy > len)
+			copy = len;
+		csum = csum_partial_copy_nocheck(skb->data + offset, to,
+						 copy, csum);
+		if ((len -= copy) == 0)
+			return csum;
+		offset += copy;
+		to     += copy;
+		pos	= copy;
+	}
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		int end;
+
+		BUG_TRAP(start <= offset + len);
+
+		end = start + skb_shinfo(skb)->frags[i].size;
+		if ((copy = end - offset) > 0) {
+			unsigned int csum2;
+			u8 *vaddr;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+			if (copy > len)
+				copy = len;
+			vaddr = kmap_skb_frag(frag);
+			csum2 = csum_partial_copy_nocheck(vaddr +
+							  frag->page_offset +
+							  offset - start, to,
+							  copy, 0);
+			kunmap_skb_frag(vaddr);
+			csum = csum_block_add(csum, csum2, pos);
+			if (!(len -= copy))
+				return csum;
+			offset += copy;
+			to     += copy;
+			pos    += copy;
+		}
+		start = end;
+	}
+
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+		for (; list; list = list->next) {
+			unsigned int csum2;
+			int end;
+
+			BUG_TRAP(start <= offset + len);
+
+			end = start + list->len;
+			if ((copy = end - offset) > 0) {
+				if (copy > len)
+					copy = len;
+				csum2 = skb_copy_and_csum_bits(list,
+							       offset - start,
+							       to, copy, 0);
+				csum = csum_block_add(csum, csum2, pos);
+				if ((len -= copy) == 0)
+					return csum;
+				offset += copy;
+				to     += copy;
+				pos    += copy;
+			}
+			start = end;
+		}
+	}
+	if (len)
+		BUG();
+	return csum;
+}
+
+void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
+{
+	unsigned int csum;
+	long csstart;
+
+	if (skb->ip_summed == CHECKSUM_HW)
+		csstart = skb->h.raw - skb->data;
+	else
+		csstart = skb->len - skb->data_len;
+
+	if (csstart > skb->len - skb->data_len)
+		BUG();
+
+	memcpy(to, skb->data, csstart);
+
+	csum = 0;
+	if (csstart != skb->len)
+		csum = skb_copy_and_csum_bits(skb, csstart, to + csstart,
+					      skb->len - csstart, 0);
+
+	if (skb->ip_summed == CHECKSUM_HW) {
+		long csstuff = csstart + skb->csum;
+
+		*((unsigned short *)(to + csstuff)) = csum_fold(csum);
+	}
+}
+
+#if 0
+/*
+ * 	Tune the memory allocator for a new MTU size.
+ */
+void skb_add_mtu(int mtu)
+{
+	/* Must match allocation in alloc_skb */
+	mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info);
+
+	kmem_add_cache_size(mtu);
+}
+#endif
+
+void __init skb_init(void)
+{
+	int i;
+
+	skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+					      sizeof(struct sk_buff),
+					      0,
+					      SLAB_HWCACHE_ALIGN,
+					      skb_headerinit, NULL);
+	if (!skbuff_head_cache)
+		panic("cannot create skbuff cache");
+
+	for (i = 0; i < NR_CPUS; i++)
+		skb_queue_head_init(&skb_head_pool[i].list);
+}
diff --git a/br-nf-bds/linux2.5/net/ipv4/ip_output.c b/br-nf-bds/linux2.5/net/ipv4/ip_output.c
new file mode 100644
index 0000000..d41926e
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/ipv4/ip_output.c
@@ -0,0 +1,1267 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		The Internet Protocol (IP) output module.
+ *
+ * Version:	$Id: ip_output.c,v 1.7 2002/10/21 17:45:17 bdschuym Exp $
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@super.org>
+ *		Alan Cox, <Alan.Cox@linux.org>
+ *		Richard Underwood
+ *		Stefan Becker, <stefanb@yello.ping.de>
+ *		Jorge Cwik, <jorge@laser.satlink.net>
+ *		Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *		Hirokazu Takahashi, <taka@valinux.co.jp>
+ *
+ *	See ip_input.c for original log
+ *
+ *	Fixes:
+ *		Alan Cox	:	Missing nonblock feature in ip_build_xmit.
+ *		Mike Kilburn	:	htons() missing in ip_build_xmit.
+ *		Bradford Johnson:	Fix faulty handling of some frames when 
+ *					no route is found.
+ *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
+ *					(in case if packet not accepted by
+ *					output firewall rules)
+ *		Mike McLagan	:	Routing by source
+ *		Alexey Kuznetsov:	use new route cache
+ *		Andi Kleen:		Fix broken PMTU recovery and remove
+ *					some redundant tests.
+ *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
+ *		Andi Kleen	: 	Replace ip_reply with ip_send_reply.
+ *		Andi Kleen	:	Split fast and slow ip_build_xmit path 
+ *					for decreased register pressure on x86 
+ *					and more readibility. 
+ *		Marc Boucher	:	When call_out_firewall returns FW_QUEUE,
+ *					silently drop skb instead of failing with -EPERM.
+ *		Detlev Wengorz	:	Copy protocol for fragments.
+ *		Hirokazu Takahashi:	HW checksumming for outgoing UDP
+ *					datagrams.
+ *		Hirokazu Takahashi:	sendfile() on UDP works now.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <net/inetpeer.h>
+#include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/mroute.h>
+#include <linux/netlink.h>
+
+/*
+ *      Shall we try to damage output packets if routing dev changes?
+ */
+
+int sysctl_ip_dynaddr = 0;
+int sysctl_ip_default_ttl = IPDEFTTL;
+
+/* Generate a checksum for an outgoing IP datagram. */
+__inline__ void ip_send_check(struct iphdr *iph)
+{
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+}
+
+/* dev_loopback_xmit for use with netfilter. */
+static int ip_dev_loopback_xmit(struct sk_buff *newskb)
+{
+	newskb->mac.raw = newskb->data;
+	__skb_pull(newskb, newskb->nh.raw - newskb->data);
+	newskb->pkt_type = PACKET_LOOPBACK;
+	newskb->ip_summed = CHECKSUM_UNNECESSARY;
+	BUG_TRAP(newskb->dst);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	nf_debug_ip_loopback_xmit(newskb);
+#endif
+	netif_rx(newskb);
+	return 0;
+}
+
+/* 
+ *		Add an ip header to a skbuff and send it out.
+ *
+ */
+int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+			  u32 saddr, u32 daddr, struct ip_options *opt)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct rtable *rt = (struct rtable *)skb->dst;
+	struct iphdr *iph;
+
+	/* Build the IP header. */
+	if (opt)
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
+	else
+		iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = inet->tos;
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		iph->frag_off = htons(IP_DF);
+	else
+		iph->frag_off = 0;
+	iph->ttl      = inet->ttl;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = sk->protocol;
+	iph->tot_len  = htons(skb->len);
+	ip_select_ident(iph, &rt->u.dst, sk);
+	skb->nh.iph   = iph;
+
+	if (opt && opt->optlen) {
+		iph->ihl += opt->optlen>>2;
+		ip_options_build(skb, opt, daddr, rt, 0);
+	}
+	ip_send_check(iph);
+
+	skb->priority = sk->priority;
+
+	/* Send it out. */
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		       dst_output);
+}
+
+static inline int ip_finish_output2(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb->dst;
+	struct hh_cache *hh = dst->hh;
+	struct net_device *dev = dst->dev;
+
+	/* Be paranoid, rather than too clever. */
+	if (unlikely(skb_headroom(skb) < dev->hard_header_len
+		     && dev->hard_header)) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len&~15) + 16);
+		if (skb2 == NULL) {
+			kfree_skb(skb);
+			return -ENOMEM;
+		}
+		if (skb->sk)
+			skb_set_owner_w(skb2, skb->sk);
+		kfree_skb(skb);
+		skb = skb2;
+	}
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	nf_debug_ip_finish_output2(skb);
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+	if (hh) {
+		read_lock_bh(&hh->hh_lock);
+  		memcpy(skb->data - 16, hh->hh_data, 16);
+		read_unlock_bh(&hh->hh_lock);
+	        skb_push(skb, hh->hh_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	if (net_ratelimit())
+		printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+__inline__ int ip_finish_output(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dst->dev;
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_IP);
+
+	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
+		       ip_finish_output2);
+}
+
+int ip_mc_output(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct net_device *dev = rt->u.dst.dev;
+
+	/*
+	 *	If the indicated interface is up and running, send the packet.
+	 */
+	IP_INC_STATS(IpOutRequests);
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_IP);
+
+	/*
+	 *	Multicasts are looped back for other local users
+	 */
+
+	if (rt->rt_flags&RTCF_MULTICAST) {
+		if ((!sk || inet_sk(sk)->mc_loop)
+#ifdef CONFIG_IP_MROUTE
+		/* Small optimization: do not loopback not local frames,
+		   which returned after forwarding; they will be  dropped
+		   by ip_mr_input in any case.
+		   Note, that local frames are looped back to be delivered
+		   to local recipients.
+
+		   This check is duplicated in ip_mr_input at the moment.
+		 */
+		    && ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))
+#endif
+		) {
+			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+			if (newskb)
+				NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
+					newskb->dev, 
+					ip_dev_loopback_xmit);
+		}
+
+		/* Multicasts with ttl 0 must not go beyond the host */
+
+		if (skb->nh.iph->ttl == 0) {
+			kfree_skb(skb);
+			return 0;
+		}
+	}
+
+	if (rt->rt_flags&RTCF_BROADCAST) {
+		struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+		if (newskb)
+			NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
+				newskb->dev, ip_dev_loopback_xmit);
+	}
+
+	if (skb->len > dev->mtu || skb_shinfo(skb)->frag_list)
+		return ip_fragment(skb, ip_finish_output);
+	else
+		return ip_finish_output(skb);
+}
+
+int ip_output(struct sk_buff *skb)
+{
+	IP_INC_STATS(IpOutRequests);
+
+	if ((skb->len > skb->dst->dev->mtu || skb_shinfo(skb)->frag_list) &&
+	    !skb_shinfo(skb)->tso_size)
+		return ip_fragment(skb, ip_finish_output);
+	else
+		return ip_finish_output(skb);
+}
+
+int ip_queue_xmit(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_options *opt = inet->opt;
+	struct rtable *rt;
+	struct iphdr *iph;
+
+	/* Skip all of this if the packet is already routed,
+	 * f.e. by something like SCTP.
+	 */
+	rt = (struct rtable *) skb->dst;
+	if (rt != NULL)
+		goto packet_routed;
+
+	/* Make sure we can route this packet. */
+	rt = (struct rtable *)__sk_dst_check(sk, 0);
+	if (rt == NULL) {
+		u32 daddr;
+
+		/* Use correct destination address if we have options. */
+		daddr = inet->daddr;
+		if(opt && opt->srr)
+			daddr = opt->faddr;
+
+		{
+			struct flowi fl = { .nl_u = { .ip4_u =
+						      { .daddr = daddr,
+							.saddr = inet->saddr,
+							.tos = RT_CONN_FLAGS(sk) } },
+					    .oif = sk->bound_dev_if };
+
+			/* If this fails, retransmit mechanism of transport layer will
+			 * keep trying until route appears or the connection times itself
+			 * out.
+			 */
+			if (ip_route_output_key(&rt, &fl))
+				goto no_route;
+		}
+		__sk_dst_set(sk, &rt->u.dst);
+		tcp_v4_setup_caps(sk, &rt->u.dst);
+	}
+	skb->dst = dst_clone(&rt->u.dst);
+
+packet_routed:
+	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+		goto no_route;
+
+	/* OK, we know where to send it, allocate and build IP header. */
+	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
+	iph->tot_len = htons(skb->len);
+	if (ip_dont_fragment(sk, &rt->u.dst))
+		iph->frag_off = htons(IP_DF);
+	else
+		iph->frag_off = 0;
+	iph->ttl      = inet->ttl;
+	iph->protocol = sk->protocol;
+	iph->saddr    = rt->rt_src;
+	iph->daddr    = rt->rt_dst;
+	skb->nh.iph   = iph;
+	/* Transport layer set skb->h.foo itself. */
+
+	if(opt && opt->optlen) {
+		iph->ihl += opt->optlen >> 2;
+		ip_options_build(skb, opt, inet->daddr, rt, 0);
+	}
+
+	if (skb->len > rt->u.dst.pmtu && (sk->route_caps&NETIF_F_TSO)) {
+		unsigned int hlen;
+
+		/* Hack zone: all this must be done by TCP. */
+		hlen = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+		skb_shinfo(skb)->tso_size = rt->u.dst.pmtu - hlen;
+		skb_shinfo(skb)->tso_segs =
+			(skb->len - hlen + skb_shinfo(skb)->tso_size - 1)/
+				skb_shinfo(skb)->tso_size - 1;
+	}
+
+	ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);
+
+	/* Add an IP checksum. */
+	ip_send_check(iph);
+
+	skb->priority = sk->priority;
+
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
+		       dst_output);
+
+no_route:
+	IP_INC_STATS(IpOutNoRoutes);
+	kfree_skb(skb);
+	return -EHOSTUNREACH;
+}
+
+
+static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
+{
+	to->pkt_type = from->pkt_type;
+	to->priority = from->priority;
+	to->protocol = from->protocol;
+	to->security = from->security;
+	to->dst = dst_clone(from->dst);
+	to->dev = from->dev;
+
+	/* Copy the flags to each fragment. */
+	IPCB(to)->flags = IPCB(from)->flags;
+
+#ifdef CONFIG_NET_SCHED
+	to->tc_index = from->tc_index;
+#endif
+#ifdef CONFIG_NETFILTER
+	to->nfmark = from->nfmark;
+	/* Connection association is same as pre-frag packet */
+	to->nfct = from->nfct;
+	nf_conntrack_get(to->nfct);
+	to->nf_bridge = from->nf_bridge;
+	nf_bridge_get(to->nf_bridge);
+#ifdef CONFIG_NETFILTER_DEBUG
+	to->nf_debug = from->nf_debug;
+#endif
+#endif
+}
+
+/*
+ *	This IP datagram is too large to be sent in one piece.  Break it up into
+ *	smaller pieces (each of size equal to IP header plus
+ *	a block of the data of the original IP data part) that will yet fit in a
+ *	single device frame, and queue such a frame for sending.
+ */
+
+int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+{
+	struct iphdr *iph;
+	int raw = 0;
+	int ptr;
+	struct net_device *dev;
+	struct sk_buff *skb2;
+	unsigned int mtu, hlen, left, len; 
+	int offset;
+	int not_last_frag;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	int err = 0;
+
+	dev = rt->u.dst.dev;
+
+	/*
+	 *	Point into the IP datagram header.
+	 */
+
+	iph = skb->nh.iph;
+
+	if (unlikely(iph->frag_off & htons(IP_DF))) {
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+			  htonl(rt->u.dst.pmtu));
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
+	/*
+	 *	Setup starting values.
+	 */
+
+	hlen = iph->ihl * 4;
+	mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
+
+	/* When frag_list is given, use it. First, check its validity:
+	 * some transformers could create wrong frag_list or break existing
+	 * one, it is not prohibited. In this case fall back to copying.
+	 *
+	 * LATER: this step can be merged to real generation of fragments,
+	 * we can switch to copy when see the first bad fragment.
+	 */
+	if (skb_shinfo(skb)->frag_list) {
+		struct sk_buff *frag;
+		int first_len = skb_pagelen(skb);
+
+		if (first_len - hlen > mtu ||
+		    ((first_len - hlen) & 7) ||
+		    (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||
+		    skb_cloned(skb))
+			goto slow_path;
+
+		for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+			/* Correct geometry. */
+			if (frag->len > mtu ||
+			    ((frag->len & 7) && frag->next) ||
+			    skb_headroom(frag) < hlen)
+			    goto slow_path;
+
+			/* Correct socket ownership. */
+			if (frag->sk == NULL)
+				goto slow_path;
+
+			/* Partially cloned skb? */
+			if (skb_shared(frag))
+				goto slow_path;
+		}
+
+		/* Everything is OK. Generate! */
+
+		err = 0;
+		offset = 0;
+		frag = skb_shinfo(skb)->frag_list;
+		skb_shinfo(skb)->frag_list = 0;
+		skb->data_len = first_len - skb_headlen(skb);
+		skb->len = first_len;
+		iph->tot_len = htons(first_len);
+		iph->frag_off |= htons(IP_MF);
+		ip_send_check(iph);
+
+		for (;;) {
+			/* Prepare header of the next frame,
+			 * before previous one went down. */
+			if (frag) {
+				frag->h.raw = frag->data;
+				frag->nh.raw = __skb_push(frag, hlen);
+				memcpy(frag->nh.raw, iph, hlen);
+				iph = frag->nh.iph;
+				iph->tot_len = htons(frag->len);
+				ip_copy_metadata(frag, skb);
+				if (offset == 0)
+					ip_options_fragment(frag);
+				offset += skb->len - hlen;
+				iph->frag_off = htons(offset>>3);
+				if (frag->next != NULL)
+					iph->frag_off |= htons(IP_MF);
+				/* Ready, complete checksum */
+				ip_send_check(iph);
+			}
+
+			err = output(skb);
+
+			if (err || !frag)
+				break;
+
+			skb = frag;
+			frag = skb->next;
+			skb->next = NULL;
+		}
+
+		if (err == 0) {
+			IP_INC_STATS(IpFragOKs);
+			return 0;
+		}
+
+		while (frag) {
+			skb = frag->next;
+			kfree_skb(frag);
+			frag = skb;
+		}
+		IP_INC_STATS(IpFragFails);
+		return err;
+	}
+
+slow_path:
+	left = skb->len - hlen;		/* Space per frame */
+	ptr = raw + hlen;		/* Where to start from */
+
+	/*
+	 *	Fragment the datagram.
+	 */
+
+	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+	not_last_frag = iph->frag_off & htons(IP_MF);
+
+	/*
+	 *	Keep copying data until we run out.
+	 */
+
+	while(left > 0)	{
+		len = left;
+		/* IF: it doesn't fit, use 'mtu' - the data space left */
+		if (len > mtu)
+			len = mtu;
+		/* IF: we are not sending upto and including the packet end
+		   then align the next start on an eight byte boundary */
+		if (len < left)	{
+			len &= ~7;
+		}
+		/*
+		 *	Allocate buffer.
+		 */
+
+		if ((skb2 = alloc_skb(len+hlen+rt->u.dst.dev->hard_header_len+16,GFP_ATOMIC)) == NULL) {
+			NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+			err = -ENOMEM;
+			goto fail;
+		}
+
+		/*
+		 *	Set up data on packet
+		 */
+
+		ip_copy_metadata(skb2, skb);
+		skb_reserve(skb2, (rt->u.dst.dev->hard_header_len&~15)+16);
+		skb_put(skb2, len + hlen);
+		skb2->nh.raw = skb2->data;
+		skb2->h.raw = skb2->data + hlen;
+
+		/*
+		 *	Charge the memory for the fragment to any owner
+		 *	it might possess
+		 */
+
+		if (skb->sk)
+			skb_set_owner_w(skb2, skb->sk);
+
+		/*
+		 *	Copy the packet header into the new buffer.
+		 */
+
+		memcpy(skb2->nh.raw, skb->data, hlen);
+
+		/*
+		 *	Copy a block of the IP datagram.
+		 */
+		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))
+			BUG();
+		left -= len;
+
+		/*
+		 *	Fill in the new header fields.
+		 */
+		iph = skb2->nh.iph;
+		iph->frag_off = htons((offset >> 3));
+
+		/* ANK: dirty, but effective trick. Upgrade options only if
+		 * the segment to be fragmented was THE FIRST (otherwise,
+		 * options are already fixed) and make it ONCE
+		 * on the initial skb, so that all the following fragments
+		 * will inherit fixed options.
+		 */
+		if (offset == 0)
+			ip_options_fragment(skb);
+
+		/*
+		 *	Added AC : If we are fragmenting a fragment that's not the
+		 *		   last fragment then keep MF on each bit
+		 */
+		if (left > 0 || not_last_frag)
+			iph->frag_off |= htons(IP_MF);
+		ptr += len;
+		offset += len;
+
+		/*
+		 *	Put this fragment into the sending queue.
+		 */
+
+		IP_INC_STATS(IpFragCreates);
+
+		iph->tot_len = htons(len + hlen);
+
+		ip_send_check(iph);
+
+		err = output(skb2);
+		if (err)
+			goto fail;
+	}
+	kfree_skb(skb);
+	IP_INC_STATS(IpFragOKs);
+	return err;
+
+fail:
+	kfree_skb(skb); 
+	IP_INC_STATS(IpFragFails);
+	return err;
+}
+
+int
+ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
+{
+	struct iovec *iov = from;
+
+	if (skb->ip_summed == CHECKSUM_HW) {
+		if (memcpy_fromiovecend(to, iov, offset, len) < 0)
+			return -EFAULT;
+	} else {
+		unsigned int csum = 0;
+		if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0)
+			return -EFAULT;
+		skb->csum = csum_block_add(skb->csum, csum, odd);
+	}
+	return 0;
+}
+
+static inline int
+skb_can_coalesce(struct sk_buff *skb, int i, struct page *page, int off)
+{
+	if (i) {
+		skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];
+		return page == frag->page &&
+			off == frag->page_offset+frag->size;
+	}
+	return 0;
+}
+
+static inline void
+skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size)
+{
+	skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+	frag->page = page;
+	frag->page_offset = off;
+	frag->size = size;
+	skb_shinfo(skb)->nr_frags = i+1;
+}
+
+static inline unsigned int
+csum_page(struct page *page, int offset, int copy)
+{
+	char *kaddr;
+	unsigned int csum;
+	kaddr = kmap(page);
+	csum = csum_partial(kaddr + offset, copy, 0);
+	kunmap(page);
+	return csum;
+}
+
+/*
+ *	ip_append_data() and ip_append_page() can make one large IP datagram
+ *	from many pieces of data. Each pieces will be holded on the socket
+ *	until ip_push_pending_frames() is called. Eache pieces can be a page
+ *	or non-page data.
+ *	
+ *	Not only UDP, other transport protocols - e.g. raw sockets - can use
+ *	this interface potentially.
+ *
+ *	LATER: length must be adjusted by pad at tail, when it is required.
+ */
+int ip_append_data(struct sock *sk,
+		   int getfrag(void *from, char *to, int offset, int len,
+			       int odd, struct sk_buff *skb),
+		   void *from, int length, int transhdrlen,
+		   struct ipcm_cookie *ipc, struct rtable *rt,
+		   unsigned int flags)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct sk_buff *skb;
+
+	struct ip_options *opt = NULL;
+	int hh_len;
+	int exthdrlen;
+	int mtu;
+	int copy;
+	int err;
+	int offset = 0;
+	unsigned int maxfraglen, fragheaderlen;
+	int csummode = CHECKSUM_NONE;
+
+	if (flags&MSG_PROBE)
+		return 0;
+
+	if (skb_queue_empty(&sk->write_queue)) {
+		/*
+		 * setup for corking.
+		 */
+		opt = ipc->opt;
+		if (opt) {
+			if (inet->cork.opt == NULL)
+				inet->cork.opt = kmalloc(sizeof(struct ip_options)+40, sk->allocation);
+			memcpy(inet->cork.opt, opt, sizeof(struct ip_options)+opt->optlen);
+			inet->cork.flags |= IPCORK_OPT;
+			inet->cork.addr = ipc->addr;
+		}
+		dst_hold(&rt->u.dst);
+		inet->cork.fragsize = mtu = rt->u.dst.pmtu;
+		inet->cork.rt = rt;
+		inet->cork.length = 0;
+		inet->sndmsg_page = NULL;
+		inet->sndmsg_off = 0;
+		if ((exthdrlen = rt->u.dst.header_len) != 0) {
+			length += exthdrlen;
+			transhdrlen += exthdrlen;
+		}
+	} else {
+		rt = inet->cork.rt;
+		if (inet->cork.flags & IPCORK_OPT)
+			opt = inet->cork.opt;
+
+		transhdrlen = 0;
+		exthdrlen = 0;
+		mtu = inet->cork.fragsize;
+	}
+	hh_len = (rt->u.dst.dev->hard_header_len&~15) + 16;
+
+	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
+	maxfraglen = ((mtu-fragheaderlen) & ~7) + fragheaderlen;
+
+	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);
+		return -EMSGSIZE;
+	}
+
+	/*
+	 * transhdrlen > 0 means that this is the first fragment and we wish
+	 * it won't be fragmented in the future.
+	 */
+	if (transhdrlen &&
+	    length + fragheaderlen <= maxfraglen &&
+	    rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) &&
+	    !exthdrlen)
+		csummode = CHECKSUM_HW;
+
+	inet->cork.length += length;
+
+	if ((skb = skb_peek_tail(&sk->write_queue)) == NULL)
+		goto alloc_new_skb;
+
+	while (length > 0) {
+		if ((copy = maxfraglen - skb->len) <= 0) {
+			char *data;
+			unsigned int datalen;
+			unsigned int fraglen;
+			unsigned int alloclen;
+			BUG_TRAP(copy == 0);
+
+alloc_new_skb:
+			datalen = maxfraglen - fragheaderlen;
+			if (datalen > length)
+				datalen = length;
+
+			fraglen = datalen + fragheaderlen;
+			if ((flags & MSG_MORE) && 
+			    !(rt->u.dst.dev->features&NETIF_F_SG))
+				alloclen = maxfraglen;
+			else
+				alloclen = datalen + fragheaderlen;
+			if (!(flags & MSG_DONTWAIT) || transhdrlen) {
+				skb = sock_alloc_send_skb(sk, 
+						alloclen + hh_len + 15,
+						(flags & MSG_DONTWAIT), &err);
+			} else {
+				skb = sock_wmalloc(sk, 
+						alloclen + hh_len + 15, 1,
+						sk->allocation);
+				if (unlikely(skb == NULL))
+					err = -ENOBUFS;
+			}
+			if (skb == NULL)
+				goto error;
+
+			/*
+			 *	Fill in the control structures
+			 */
+			skb->ip_summed = csummode;
+			skb->csum = 0;
+			skb_reserve(skb, hh_len);
+
+			/*
+			 *	Find where to start putting bytes.
+			 */
+			data = skb_put(skb, fraglen);
+			skb->nh.raw = __skb_pull(skb, exthdrlen);
+			data += fragheaderlen;
+			skb->h.raw = data + exthdrlen;
+
+			copy = datalen - transhdrlen;
+			if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, 0, skb) < 0) {
+				err = -EFAULT;
+				kfree_skb(skb);
+				goto error;
+			}
+
+			offset += copy;
+			length -= datalen;
+			transhdrlen = 0;
+			exthdrlen = 0;
+			csummode = CHECKSUM_NONE;
+
+			/*
+			 * Put the packet on the pending queue.
+			 */
+			__skb_queue_tail(&sk->write_queue, skb);
+			continue;
+		}
+
+		if (copy > length)
+			copy = length;
+
+		if (!(rt->u.dst.dev->features&NETIF_F_SG)) {
+			unsigned int off;
+
+			off = skb->len;
+			if (getfrag(from, skb_put(skb, copy), 
+					offset, copy, off, skb) < 0) {
+				__skb_trim(skb, off);
+				err = -EFAULT;
+				goto error;
+			}
+		} else {
+			int i = skb_shinfo(skb)->nr_frags;
+			skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];
+			struct page *page = inet->sndmsg_page;
+			int off = inet->sndmsg_off;
+			unsigned int left;
+
+			if (page && (left = PAGE_SIZE - off) > 0) {
+				if (copy >= left)
+					copy = left;
+				if (page != frag->page) {
+					if (i == MAX_SKB_FRAGS) {
+						err = -EMSGSIZE;
+						goto error;
+					}
+					get_page(page);
+	 				skb_fill_page_desc(skb, i, page, inet->sndmsg_off, 0);
+					frag = &skb_shinfo(skb)->frags[i];
+				}
+			} else if (i < MAX_SKB_FRAGS) {
+				if (copy > PAGE_SIZE)
+					copy = PAGE_SIZE;
+				page = alloc_pages(sk->allocation, 0);
+				if (page == NULL)  {
+					err = -ENOMEM;
+					goto error;
+				}
+				inet->sndmsg_page = page;
+				inet->sndmsg_off = 0;
+
+				skb_fill_page_desc(skb, i, page, 0, 0);
+				frag = &skb_shinfo(skb)->frags[i];
+				skb->truesize += PAGE_SIZE;
+				atomic_add(PAGE_SIZE, &sk->wmem_alloc);
+			} else {
+				err = -EMSGSIZE;
+				goto error;
+			}
+			if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) < 0) {
+				err = -EFAULT;
+				goto error;
+			}
+			inet->sndmsg_off += copy;
+			frag->size += copy;
+			skb->len += copy;
+			skb->data_len += copy;
+		}
+		offset += copy;
+		length -= copy;
+	}
+
+	return 0;
+
+error:
+	inet->cork.length -= length;
+	IP_INC_STATS(IpOutDiscards);
+	return err; 
+}
+
+ssize_t	ip_append_page(struct sock *sk, struct page *page,
+		       int offset, size_t size, int flags)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct sk_buff *skb;
+	struct rtable *rt;
+	struct ip_options *opt = NULL;
+	int hh_len;
+	int mtu;
+	int len;
+	int err;
+	unsigned int maxfraglen, fragheaderlen;
+
+	if (inet->hdrincl)
+		return -EPERM;
+
+	if (flags&MSG_PROBE)
+		return 0;
+
+	if (skb_queue_empty(&sk->write_queue))
+		return -EINVAL;
+
+	rt = inet->cork.rt;
+	if (inet->cork.flags & IPCORK_OPT)
+		opt = inet->cork.opt;
+
+	if (!(rt->u.dst.dev->features&NETIF_F_SG))
+		return -EOPNOTSUPP;
+
+	hh_len = (rt->u.dst.dev->hard_header_len&~15)+16;
+	mtu = inet->cork.fragsize;
+
+	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
+	maxfraglen = ((mtu-fragheaderlen) & ~7) + fragheaderlen;
+
+	if (inet->cork.length + size > 0xFFFF - fragheaderlen) {
+		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu);
+		return -EMSGSIZE;
+	}
+
+	if ((skb = skb_peek_tail(&sk->write_queue)) == NULL)
+		return -EINVAL;
+
+	inet->cork.length += size;
+
+	while (size > 0) {
+		int i;
+		if ((len = maxfraglen - skb->len) <= 0) {
+			char *data;
+			struct iphdr *iph;
+			BUG_TRAP(len == 0);
+
+			skb = sock_wmalloc(sk, fragheaderlen + hh_len + 15, 1,
+					   sk->allocation);
+			if (unlikely(!skb)) {
+				err = -ENOBUFS;
+				goto error;
+			}
+
+			/*
+			 *	Fill in the control structures
+			 */
+			skb->ip_summed = CHECKSUM_NONE;
+			skb->csum = 0;
+			skb_reserve(skb, hh_len);
+
+			/*
+			 *	Find where to start putting bytes.
+			 */
+			data = skb_put(skb, fragheaderlen);
+			skb->nh.iph = iph = (struct iphdr *)data;
+			data += fragheaderlen;
+			skb->h.raw = data;
+
+			/*
+			 * Put the packet on the pending queue.
+			 */
+			__skb_queue_tail(&sk->write_queue, skb);
+			continue;
+		}
+
+		i = skb_shinfo(skb)->nr_frags;
+		if (len > size)
+			len = size;
+		if (skb_can_coalesce(skb, i, page, offset)) {
+			skb_shinfo(skb)->frags[i-1].size += len;
+		} else if (i < MAX_SKB_FRAGS) {
+			get_page(page);
+			skb_fill_page_desc(skb, i, page, offset, len);
+		} else {
+			err = -EMSGSIZE;
+			goto error;
+		}
+
+		if (skb->ip_summed == CHECKSUM_NONE) {
+			unsigned int csum;
+			csum = csum_page(page, offset, len);
+			skb->csum = csum_block_add(skb->csum, csum, skb->len);
+		}
+
+		skb->len += len;
+		skb->data_len += len;
+		offset += len;
+		size -= len;
+	}
+	return 0;
+
+error:
+	inet->cork.length -= size;
+	IP_INC_STATS(IpOutDiscards);
+	return err;
+}
+
+/*
+ *	Combined all pending IP fragments on the socket as one IP datagram
+ *	and push them out.
+ */
+int ip_push_pending_frames(struct sock *sk)
+{
+	struct sk_buff *skb, *tmp_skb;
+	struct sk_buff **tail_skb;
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_options *opt = NULL;
+	struct rtable *rt = inet->cork.rt;
+	struct iphdr *iph;
+	int df = 0;
+	__u8 ttl;
+	int err = 0;
+
+	if ((skb = __skb_dequeue(&sk->write_queue)) == NULL)
+		goto out;
+	tail_skb = &(skb_shinfo(skb)->frag_list);
+
+	while ((tmp_skb = __skb_dequeue(&sk->write_queue)) != NULL) {
+		__skb_pull(tmp_skb, skb->h.raw - skb->nh.raw);
+		*tail_skb = tmp_skb;
+		tail_skb = &(tmp_skb->next);
+		skb->len += tmp_skb->len;
+		skb->data_len += tmp_skb->len;
+#if 0 /* Logically correct, but useless work, ip_fragment() will have to undo */
+		skb->truesize += tmp_skb->truesize;
+		__sock_put(tmp_skb->sk);
+		tmp_skb->destructor = NULL;
+		tmp_skb->sk = NULL;
+#endif
+	}
+
+	if (inet->pmtudisc == IP_PMTUDISC_DO ||
+	    (!skb_shinfo(skb)->frag_list && ip_dont_fragment(sk, &rt->u.dst)))
+		df = htons(IP_DF);
+
+	if (inet->cork.flags & IPCORK_OPT)
+		opt = inet->cork.opt;
+
+	if (rt->rt_type == RTN_MULTICAST)
+		ttl = inet->mc_ttl;
+	else
+		ttl = inet->ttl;
+
+	iph = (struct iphdr *)skb->data;
+	iph->version = 4;
+	iph->ihl = 5;
+	if (opt) {
+		iph->ihl += opt->optlen>>2;
+		ip_options_build(skb, opt, inet->cork.addr, rt, 0);
+	}
+	iph->tos = inet->tos;
+	iph->tot_len = htons(skb->len);
+	iph->frag_off = df;
+	if (!df) {
+		__ip_select_ident(iph, &rt->u.dst, 0);
+	} else {
+		iph->id = htons(inet->id++);
+	}
+	iph->ttl = ttl;
+	iph->protocol = sk->protocol;
+	iph->saddr = rt->rt_src;
+	iph->daddr = rt->rt_dst;
+	ip_send_check(iph);
+
+	skb->priority = sk->priority;
+	skb->dst = dst_clone(&rt->u.dst);
+
+	/* Netfilter gets whole the not fragmented skb. */
+	err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, 
+		      skb->dst->dev, dst_output);
+	if (err) {
+		if (err > 0)
+			err = inet->recverr ? net_xmit_errno(err) : 0;
+		if (err)
+			goto error;
+	}
+
+out:
+	inet->cork.flags &= ~IPCORK_OPT;
+	if (inet->cork.rt) {
+		ip_rt_put(inet->cork.rt);
+		inet->cork.rt = NULL;
+	}
+	return err;
+
+error:
+	IP_INC_STATS(IpOutDiscards);
+	goto out;
+}
+
+/*
+ *	Throw away all pending data on the socket.
+ */
+void ip_flush_pending_frames(struct sock *sk)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct sk_buff *skb;
+
+	while ((skb = __skb_dequeue_tail(&sk->write_queue)) != NULL)
+		kfree_skb(skb);
+
+	inet->cork.flags &= ~IPCORK_OPT;
+	if (inet->cork.opt) {
+		kfree(inet->cork.opt);
+		inet->cork.opt = NULL;
+	}
+	if (inet->cork.rt) {
+		ip_rt_put(inet->cork.rt);
+		inet->cork.rt = NULL;
+	}
+}
+
+
+/*
+ *	Fetch data from kernel space and fill in checksum if needed.
+ */
+static int ip_reply_glue_bits(void *dptr, char *to, int offset, 
+			      int len, int odd, struct sk_buff *skb)
+{
+	unsigned int csum;
+
+	csum = csum_partial_copy_nocheck(dptr+offset, to, len, 0);
+	skb->csum = csum_block_add(skb->csum, csum, odd);
+	return 0;  
+}
+
+/* 
+ *	Generic function to send a packet as reply to another packet.
+ *	Used to send TCP resets so far. ICMP should use this function too.
+ *
+ *	Should run single threaded per socket because it uses the sock 
+ *     	structure to pass arguments.
+ *
+ *	LATER: switch from ip_build_xmit to ip_append_*
+ */
+void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
+		   unsigned int len)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct {
+		struct ip_options	opt;
+		char			data[40];
+	} replyopts;
+	struct ipcm_cookie ipc;
+	u32 daddr;
+	struct rtable *rt = (struct rtable*)skb->dst;
+
+	if (ip_options_echo(&replyopts.opt, skb))
+		return;
+
+	daddr = ipc.addr = rt->rt_src;
+	ipc.opt = NULL;
+
+	if (replyopts.opt.optlen) {
+		ipc.opt = &replyopts.opt;
+
+		if (ipc.opt->srr)
+			daddr = replyopts.opt.faddr;
+	}
+
+	{
+		struct flowi fl = { .nl_u = { .ip4_u =
+					      { .daddr = daddr,
+						.saddr = rt->rt_spec_dst,
+						.tos = RT_TOS(skb->nh.iph->tos) } } };
+		if (ip_route_output_key(&rt, &fl))
+			return;
+	}
+
+	/* And let IP do all the hard work.
+
+	   This chunk is not reenterable, hence spinlock.
+	   Note that it uses the fact, that this function is called
+	   with locally disabled BH and that sk cannot be already spinlocked.
+	 */
+	bh_lock_sock(sk);
+	inet->tos = skb->nh.iph->tos;
+	sk->priority = skb->priority;
+	sk->protocol = skb->nh.iph->protocol;
+	ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
+		       &ipc, rt, MSG_DONTWAIT);
+	if ((skb = skb_peek(&sk->write_queue)) != NULL) {
+		if (arg->csumoffset >= 0)
+			*((u16 *)skb->h.raw + arg->csumoffset) = csum_fold(csum_add(skb->csum, arg->csum));
+		skb->ip_summed = CHECKSUM_NONE;
+		ip_push_pending_frames(sk);
+	}
+
+	bh_unlock_sock(sk);
+
+	ip_rt_put(rt);
+}
+
+/*
+ *	IP protocol layer initialiser
+ */
+
+static struct packet_type ip_packet_type =
+{
+	__constant_htons(ETH_P_IP),
+	NULL,	/* All devices */
+	ip_rcv,
+	(void*)1,
+	NULL,
+};
+
+/*
+ *	IP registers the packet type and then calls the subprotocol initialisers
+ */
+
+void __init ip_init(void)
+{
+	dev_add_pack(&ip_packet_type);
+
+	ip_rt_init();
+	inet_initpeers();
+
+#ifdef CONFIG_IP_MULTICAST
+	proc_net_create("igmp", 0, ip_mc_procinfo);
+#endif
+}
diff --git a/br-nf-bds/linux2.5/net/ipv4/netfilter/ip_tables.c b/br-nf-bds/linux2.5/net/ipv4/netfilter/ip_tables.c
new file mode 100644
index 0000000..4f59e30
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/ipv4/netfilter/ip_tables.c
@@ -0,0 +1,1817 @@
+/*
+ * Packet matching code.
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * Copyright (C) 2009-2002 Netfilter core team <coreteam@netfilter.org>
+ *
+ * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
+ * 	- increase module usage count as soon as we have rules inside
+ * 	  a table
+ */
+#include <linux/config.h>
+#include <linux/cache.h>
+#include <linux/skbuff.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/*#define DEBUG_IP_FIREWALL*/
+/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+/*#define DEBUG_IP_FIREWALL_USER*/
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf(format, args...)  printk(format , ## args)
+#else
+#define dprintf(format, args...)
+#endif
+
+#ifdef DEBUG_IP_FIREWALL_USER
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#define IP_NF_ASSERT(x)						\
+do {								\
+	if (!(x))						\
+		printk("IP_NF_ASSERT: %s:%s:%u\n",		\
+		       __FUNCTION__, __FILE__, __LINE__);	\
+} while(0)
+#else
+#define IP_NF_ASSERT(x)
+#endif
+#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);
+
+/* Must have mutex */
+#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+/* All the better to debug you with... */
+#define static
+#define inline
+#endif
+
+/*
+   We keep a set of rules for each CPU, so we can avoid write-locking
+   them in the softirq when updating the counters and therefore
+   only need to read-lock in the softirq; doing a write_lock_bh() in user
+   context stops packets coming through and allows user context to read
+   the counters or update the rules.
+
+   To be cache friendly on SMP, we arrange them like so:
+   [ n-entries ]
+   ... cache-align padding ...
+   [ n-entries ]
+
+   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;
+	/* Initial number of entries. Needed for module usage count */
+	unsigned int initial_entries;
+
+	/* 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] ____cacheline_aligned;
+};
+
+static LIST_HEAD(ipt_target);
+static LIST_HEAD(ipt_match);
+static LIST_HEAD(ipt_tables);
+#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
+
+#ifdef CONFIG_SMP
+#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
+#else
+#define TABLE_OFFSET(t,p) 0
+#endif
+
+#if 0
+#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
+#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
+#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
+#endif
+
+/* Returns whether matches rule or not. */
+static inline int
+ip_packet_match(const struct iphdr *ip,
+		const char *indev,
+		const char *physindev,
+		const char *outdev,
+		const char *physoutdev,
+		const struct ipt_ip *ipinfo,
+		int isfrag)
+{
+	size_t i;
+	unsigned long ret, ret2;
+
+#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+
+	if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
+		  IPT_INV_SRCIP)
+	    || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
+		     IPT_INV_DSTIP)) {
+		dprintf("Source or dest mismatch.\n");
+
+		dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
+			NIPQUAD(ip->saddr),
+			NIPQUAD(ipinfo->smsk.s_addr),
+			NIPQUAD(ipinfo->src.s_addr),
+			ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
+		dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
+			NIPQUAD(ip->daddr),
+			NIPQUAD(ipinfo->dmsk.s_addr),
+			NIPQUAD(ipinfo->dst.s_addr),
+			ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
+		return 0;
+	}
+
+	/* Look for ifname matches; this should unroll nicely. */
+	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret |= (((const unsigned long *)indev)[i]
+			^ ((const unsigned long *)ipinfo->iniface)[i])
+			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+	}
+
+	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret2 |= (((const unsigned long *)physindev)[i]
+			^ ((const unsigned long *)ipinfo->iniface)[i])
+			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+	}
+
+	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+		dprintf("VIA in mismatch (%s vs %s).%s\n",
+			indev, ipinfo->iniface,
+			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+		return 0;
+	}
+
+	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret |= (((const unsigned long *)outdev)[i]
+			^ ((const unsigned long *)ipinfo->outiface)[i])
+			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+	}
+
+	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+		ret2 |= (((const unsigned long *)physoutdev)[i]
+			^ ((const unsigned long *)ipinfo->outiface)[i])
+			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+	}
+
+	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+		dprintf("VIA out mismatch (%s vs %s).%s\n",
+			outdev, ipinfo->outiface,
+			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+		return 0;
+	}
+
+	/* Check specific protocol */
+	if (ipinfo->proto
+	    && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
+		dprintf("Packet protocol %hi does not match %hi.%s\n",
+			ip->protocol, ipinfo->proto,
+			ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
+		return 0;
+	}
+
+	/* If we have a fragment rule but the packet is not a fragment
+	 * then we return zero */
+	if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
+		dprintf("Fragment rule but not fragment.%s\n",
+			ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
+		return 0;
+	}
+
+	return 1;
+}
+
+static inline int
+ip_checkentry(const struct ipt_ip *ip)
+{
+	if (ip->flags & ~IPT_F_MASK) {
+		duprintf("Unknown flag bits set: %08X\n",
+			 ip->flags & ~IPT_F_MASK);
+		return 0;
+	}
+	if (ip->invflags & ~IPT_INV_MASK) {
+		duprintf("Unknown invflag bits set: %08X\n",
+			 ip->invflags & ~IPT_INV_MASK);
+		return 0;
+	}
+	return 1;
+}
+
+static unsigned int
+ipt_error(struct sk_buff **pskb,
+	  unsigned int hooknum,
+	  const struct net_device *in,
+	  const struct net_device *out,
+	  const void *targinfo,
+	  void *userinfo)
+{
+	if (net_ratelimit())
+		printk("ip_tables: error: `%s'\n", (char *)targinfo);
+
+	return NF_DROP;
+}
+
+static inline
+int do_match(struct ipt_entry_match *m,
+	     const struct sk_buff *skb,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     int offset,
+	     const void *hdr,
+	     u_int16_t datalen,
+	     int *hotdrop)
+{
+	/* Stop iteration if it doesn't match */
+	if (!m->u.kernel.match->match(skb, in, out, m->data,
+				      offset, hdr, datalen, hotdrop))
+		return 1;
+	else
+		return 0;
+}
+
+static inline struct ipt_entry *
+get_entry(void *base, unsigned int offset)
+{
+	return (struct ipt_entry *)(base + offset);
+}
+
+/* Returns one of the generic firewall policies, like NF_ACCEPT. */
+unsigned int
+ipt_do_table(struct sk_buff **pskb,
+	     unsigned int hook,
+	     const struct net_device *in,
+	     const struct net_device *out,
+	     struct ipt_table *table,
+	     void *userdata)
+{
+	static const char nulldevname[IFNAMSIZ] = { 0 };
+	u_int16_t offset;
+	struct iphdr *ip;
+	void *protohdr;
+	u_int16_t datalen;
+	int hotdrop = 0;
+	/* Initializing verdict to NF_DROP keeps gcc happy. */
+	unsigned int verdict = NF_DROP;
+	const char *indev, *outdev;
+	const char *physindev, *physoutdev;
+	void *table_base;
+	struct ipt_entry *e, *back;
+
+	/* Initialization */
+	ip = (*pskb)->nh.iph;
+	protohdr = (u_int32_t *)ip + ip->ihl;
+	datalen = (*pskb)->len - ip->ihl * 4;
+	indev = in ? in->name : nulldevname;
+	outdev = out ? out->name : nulldevname;
+	if ((*pskb)->nf_bridge) {
+		physindev = (*pskb)->nf_bridge->physindev ?
+			(*pskb)->nf_bridge->physindev->name : nulldevname;
+		physoutdev = (*pskb)->nf_bridge->physoutdev ?
+			(*pskb)->nf_bridge->physoutdev->name : nulldevname;
+	} else {
+		physindev = nulldevname;
+		physoutdev = nulldevname;
+	}
+
+	/* We handle fragments by dealing with the first fragment as
+	 * if it was a normal packet.  All other fragments are treated
+	 * normally, except that they will NEVER match rules that ask
+	 * things we don't know, ie. tcp syn flag or ports).  If the
+	 * rule is also a fragment-specific rule, non-fragments won't
+	 * match it. */
+	offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+	read_lock_bh(&table->lock);
+	IP_NF_ASSERT(table->valid_hooks & (1 << hook));
+	table_base = (void *)table->private->entries
+		+ TABLE_OFFSET(table->private, smp_processor_id());
+	e = get_entry(table_base, table->private->hook_entry[hook]);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	/* Check noone else using our table */
+	if (((struct ipt_entry *)table_base)->comefrom != 0xdead57ac
+	    && ((struct ipt_entry *)table_base)->comefrom != 0xeeeeeeec) {
+		printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
+		       smp_processor_id(),
+		       table->name,
+		       &((struct ipt_entry *)table_base)->comefrom,
+		       ((struct ipt_entry *)table_base)->comefrom);
+	}
+	((struct ipt_entry *)table_base)->comefrom = 0x57acc001;
+#endif
+
+	/* For return from builtin chain */
+	back = get_entry(table_base, table->private->underflow[hook]);
+
+	do {
+		IP_NF_ASSERT(e);
+		IP_NF_ASSERT(back);
+		(*pskb)->nfcache |= e->nfcache;
+		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
+		    &e->ip, offset)) {
+			struct ipt_entry_target *t;
+
+			if (IPT_MATCH_ITERATE(e, do_match,
+					      *pskb, in, out,
+					      offset, protohdr,
+					      datalen, &hotdrop) != 0)
+				goto no_match;
+
+			ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
+
+			t = ipt_get_target(e);
+			IP_NF_ASSERT(t->u.kernel.target);
+			/* Standard target? */
+			if (!t->u.kernel.target->target) {
+				int v;
+
+				v = ((struct ipt_standard_target *)t)->verdict;
+				if (v < 0) {
+					/* Pop from stack? */
+					if (v != IPT_RETURN) {
+						verdict = (unsigned)(-v) - 1;
+						break;
+					}
+					e = back;
+					back = get_entry(table_base,
+							 back->comefrom);
+					continue;
+				}
+				if (table_base + v
+				    != (void *)e + e->next_offset) {
+					/* Save old back ptr in next entry */
+					struct ipt_entry *next
+						= (void *)e + e->next_offset;
+					next->comefrom
+						= (void *)back - table_base;
+					/* set back pointer to next entry */
+					back = next;
+				}
+
+				e = get_entry(table_base, v);
+			} else {
+				/* Targets which reenter must return
+                                   abs. verdicts */
+#ifdef CONFIG_NETFILTER_DEBUG
+				((struct ipt_entry *)table_base)->comefrom
+					= 0xeeeeeeec;
+#endif
+				verdict = t->u.kernel.target->target(pskb,
+								     hook,
+								     in, out,
+								     t->data,
+								     userdata);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+				if (((struct ipt_entry *)table_base)->comefrom
+				    != 0xeeeeeeec
+				    && verdict == IPT_CONTINUE) {
+					printk("Target %s reentered!\n",
+					       t->u.kernel.target->name);
+					verdict = NF_DROP;
+				}
+				((struct ipt_entry *)table_base)->comefrom
+					= 0x57acc001;
+#endif
+				/* Target might have changed stuff. */
+				ip = (*pskb)->nh.iph;
+				protohdr = (u_int32_t *)ip + ip->ihl;
+				datalen = (*pskb)->len - ip->ihl * 4;
+
+				if (verdict == IPT_CONTINUE)
+					e = (void *)e + e->next_offset;
+				else
+					/* Verdict */
+					break;
+			}
+		} else {
+
+		no_match:
+			e = (void *)e + e->next_offset;
+		}
+	} while (!hotdrop);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	((struct ipt_entry *)table_base)->comefrom = 0xdead57ac;
+#endif
+	read_unlock_bh(&table->lock);
+
+#ifdef DEBUG_ALLOW_ALL
+	return NF_ACCEPT;
+#else
+	if (hotdrop)
+		return NF_DROP;
+	else return verdict;
+#endif
+}
+
+/* If it succeeds, returns element and locks mutex */
+static inline void *
+find_inlist_lock_noload(struct list_head *head,
+			const char *name,
+			int *error,
+			struct semaphore *mutex)
+{
+	void *ret;
+
+#if 0
+	duprintf("find_inlist: searching for `%s' in %s.\n",
+		 name, head == &ipt_target ? "ipt_target"
+		 : head == &ipt_match ? "ipt_match"
+		 : head == &ipt_tables ? "ipt_tables" : "UNKNOWN");
+#endif
+
+	*error = down_interruptible(mutex);
+	if (*error != 0)
+		return NULL;
+
+	ret = list_named_find(head, name);
+	if (!ret) {
+		*error = -ENOENT;
+		up(mutex);
+	}
+	return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head,
+		 const char *name,
+		 const char *prefix,
+		 int *error,
+		 struct semaphore *mutex)
+{
+	void *ret;
+
+	ret = find_inlist_lock_noload(head, name, error, mutex);
+	if (!ret) {
+		char modulename[IPT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
+		strcpy(modulename, prefix);
+		strcat(modulename, name);
+		duprintf("find_inlist: loading `%s'.\n", modulename);
+		request_module(modulename);
+		ret = find_inlist_lock_noload(head, name, error, mutex);
+	}
+
+	return ret;
+}
+#endif
+
+static inline struct ipt_table *
+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)
+{
+	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)
+{
+	return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
+		if (((__u32 *)ip)[i])
+			return 0;
+
+	return 1;
+}
+
+/* Figures out from what hook each rule can be called: returns 0 if
+   there are loops.  Puts hook bitmask in comefrom. */
+static int
+mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
+{
+	unsigned int hook;
+
+	/* No recursion; use packet counter to save back ptrs (reset
+	   to 0 as we leave), and comefrom to save source hook bitmask */
+	for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
+		unsigned int pos = newinfo->hook_entry[hook];
+		struct ipt_entry *e
+			= (struct ipt_entry *)(newinfo->entries + pos);
+
+		if (!(valid_hooks & (1 << hook)))
+			continue;
+
+		/* Set initial back pointer. */
+		e->counters.pcnt = pos;
+
+		for (;;) {
+			struct ipt_standard_target *t
+				= (void *)ipt_get_target(e);
+
+			if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
+				printk("iptables: loop hook %u pos %u %08X.\n",
+				       hook, pos, e->comefrom);
+				return 0;
+			}
+			e->comefrom
+				|= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
+
+			/* Unconditional return/END. */
+			if (e->target_offset == sizeof(struct ipt_entry)
+			    && (strcmp(t->target.u.user.name,
+				       IPT_STANDARD_TARGET) == 0)
+			    && t->verdict < 0
+			    && unconditional(&e->ip)) {
+				unsigned int oldpos, size;
+
+				/* Return: backtrack through the last
+				   big jump. */
+				do {
+					e->comefrom ^= (1<<NF_IP_NUMHOOKS);
+#ifdef DEBUG_IP_FIREWALL_USER
+					if (e->comefrom
+					    & (1 << NF_IP_NUMHOOKS)) {
+						duprintf("Back unset "
+							 "on hook %u "
+							 "rule %u\n",
+							 hook, pos);
+					}
+#endif
+					oldpos = pos;
+					pos = e->counters.pcnt;
+					e->counters.pcnt = 0;
+
+					/* We're at the start. */
+					if (pos == oldpos)
+						goto next;
+
+					e = (struct ipt_entry *)
+						(newinfo->entries + pos);
+				} while (oldpos == pos + e->next_offset);
+
+				/* Move along one */
+				size = e->next_offset;
+				e = (struct ipt_entry *)
+					(newinfo->entries + pos + size);
+				e->counters.pcnt = pos;
+				pos += size;
+			} else {
+				int newpos = t->verdict;
+
+				if (strcmp(t->target.u.user.name,
+					   IPT_STANDARD_TARGET) == 0
+				    && newpos >= 0) {
+					/* This a jump; chase it. */
+					duprintf("Jump rule %u -> %u\n",
+						 pos, newpos);
+				} else {
+					/* ... this is a fallthru */
+					newpos = pos + e->next_offset;
+				}
+				e = (struct ipt_entry *)
+					(newinfo->entries + newpos);
+				e->counters.pcnt = pos;
+				pos = newpos;
+			}
+		}
+		next:
+		duprintf("Finished chain %u\n", hook);
+	}
+	return 1;
+}
+
+static inline int
+cleanup_match(struct ipt_entry_match *m, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+
+	if (m->u.kernel.match->destroy)
+		m->u.kernel.match->destroy(m->data,
+					   m->u.match_size - sizeof(*m));
+
+	if (m->u.kernel.match->me)
+		__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
+
+	return 0;
+}
+
+static inline int
+standard_check(const struct ipt_entry_target *t,
+	       unsigned int max_offset)
+{
+	struct ipt_standard_target *targ = (void *)t;
+
+	/* Check standard info. */
+	if (t->u.target_size
+	    != IPT_ALIGN(sizeof(struct ipt_standard_target))) {
+		duprintf("standard_check: target size %u != %u\n",
+			 t->u.target_size,
+			 IPT_ALIGN(sizeof(struct ipt_standard_target)));
+		return 0;
+	}
+
+	if (targ->verdict >= 0
+	    && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
+		duprintf("ipt_standard_check: bad verdict (%i)\n",
+			 targ->verdict);
+		return 0;
+	}
+
+	if (targ->verdict < -NF_MAX_VERDICT - 1) {
+		duprintf("ipt_standard_check: bad negative verdict (%i)\n",
+			 targ->verdict);
+		return 0;
+	}
+	return 1;
+}
+
+static inline int
+check_match(struct ipt_entry_match *m,
+	    const char *name,
+	    const struct ipt_ip *ip,
+	    unsigned int hookmask,
+	    unsigned int *i)
+{
+	int ret;
+	struct ipt_match *match;
+
+	match = 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;
+	}
+	if (match->me)
+		__MOD_INC_USE_COUNT(match->me);
+	m->u.kernel.match = match;
+	up(&ipt_mutex);
+
+	if (m->u.kernel.match->checkentry
+	    && !m->u.kernel.match->checkentry(name, ip, m->data,
+					      m->u.match_size - sizeof(*m),
+					      hookmask)) {
+		if (m->u.kernel.match->me)
+			__MOD_DEC_USE_COUNT(m->u.kernel.match->me);
+		duprintf("ip_tables: check failed for `%s'.\n",
+			 m->u.kernel.match->name);
+		return -EINVAL;
+	}
+
+	(*i)++;
+	return 0;
+}
+
+static struct ipt_target ipt_standard_target;
+
+static inline int
+check_entry(struct ipt_entry *e, const char *name, unsigned int size,
+	    unsigned int *i)
+{
+	struct ipt_entry_target *t;
+	struct ipt_target *target;
+	int ret;
+	unsigned int j;
+
+	if (!ip_checkentry(&e->ip)) {
+		duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+		return -EINVAL;
+	}
+
+	j = 0;
+	ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
+	if (ret != 0)
+		goto cleanup_matches;
+
+	t = ipt_get_target(e);
+	target = 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;
+	}
+	if (target->me)
+		__MOD_INC_USE_COUNT(target->me);
+	t->u.kernel.target = target;
+	up(&ipt_mutex);
+
+	if (t->u.kernel.target == &ipt_standard_target) {
+		if (!standard_check(t, size)) {
+			ret = -EINVAL;
+			goto cleanup_matches;
+		}
+	} else if (t->u.kernel.target->checkentry
+		   && !t->u.kernel.target->checkentry(name, e, t->data,
+						      t->u.target_size
+						      - sizeof(*t),
+						      e->comefrom)) {
+		if (t->u.kernel.target->me)
+			__MOD_DEC_USE_COUNT(t->u.kernel.target->me);
+		duprintf("ip_tables: check failed for `%s'.\n",
+			 t->u.kernel.target->name);
+		ret = -EINVAL;
+		goto cleanup_matches;
+	}
+
+	(*i)++;
+	return 0;
+
+ cleanup_matches:
+	IPT_MATCH_ITERATE(e, cleanup_match, &j);
+	return ret;
+}
+
+static inline int
+check_entry_size_and_hooks(struct ipt_entry *e,
+			   struct ipt_table_info *newinfo,
+			   unsigned char *base,
+			   unsigned char *limit,
+			   const unsigned int *hook_entries,
+			   const unsigned int *underflows,
+			   unsigned int *i)
+{
+	unsigned int h;
+
+	if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
+	    || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+		duprintf("Bad offset %p\n", e);
+		return -EINVAL;
+	}
+
+	if (e->next_offset
+	    < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
+		duprintf("checking: element %p size %u\n",
+			 e, e->next_offset);
+		return -EINVAL;
+	}
+
+	/* Check hooks & underflows */
+	for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+		if ((unsigned char *)e - base == hook_entries[h])
+			newinfo->hook_entry[h] = hook_entries[h];
+		if ((unsigned char *)e - base == underflows[h])
+			newinfo->underflow[h] = underflows[h];
+	}
+
+	/* FIXME: underflows must be unconditional, standard verdicts
+           < 0 (not IPT_RETURN). --RR */
+
+	/* Clear counters and comefrom */
+	e->counters = ((struct ipt_counters) { 0, 0 });
+	e->comefrom = 0;
+
+	(*i)++;
+	return 0;
+}
+
+static inline int
+cleanup_entry(struct ipt_entry *e, unsigned int *i)
+{
+	struct ipt_entry_target *t;
+
+	if (i && (*i)-- == 0)
+		return 1;
+
+	/* Cleanup all matches */
+	IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+	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);
+
+	return 0;
+}
+
+/* Checks and translates the user-supplied table segment (held in
+   newinfo) */
+static int
+translate_table(const char *name,
+		unsigned int valid_hooks,
+		struct ipt_table_info *newinfo,
+		unsigned int size,
+		unsigned int number,
+		const unsigned int *hook_entries,
+		const unsigned int *underflows)
+{
+	unsigned int i;
+	int ret;
+
+	newinfo->size = size;
+	newinfo->number = number;
+
+	/* Init all hooks to impossible value. */
+	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+		newinfo->hook_entry[i] = 0xFFFFFFFF;
+		newinfo->underflow[i] = 0xFFFFFFFF;
+	}
+
+	duprintf("translate_table: size %u\n", newinfo->size);
+	i = 0;
+	/* Walk through entries, checking offsets. */
+	ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				check_entry_size_and_hooks,
+				newinfo,
+				newinfo->entries,
+				newinfo->entries + size,
+				hook_entries, underflows, &i);
+	if (ret != 0)
+		return ret;
+
+	if (i != number) {
+		duprintf("translate_table: %u not %u entries\n",
+			 i, number);
+		return -EINVAL;
+	}
+
+	/* Check hooks all assigned */
+	for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+		/* Only hooks which are valid */
+		if (!(valid_hooks & (1 << i)))
+			continue;
+		if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
+			duprintf("Invalid hook entry %u %u\n",
+				 i, hook_entries[i]);
+			return -EINVAL;
+		}
+		if (newinfo->underflow[i] == 0xFFFFFFFF) {
+			duprintf("Invalid underflow %u %u\n",
+				 i, underflows[i]);
+			return -EINVAL;
+		}
+	}
+
+	if (!mark_source_chains(newinfo, valid_hooks))
+		return -ELOOP;
+
+	/* Finally, each sanity check must pass */
+	i = 0;
+	ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				check_entry, name, size, &i);
+
+	if (ret != 0) {
+		IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+				  cleanup_entry, &i);
+		return ret;
+	}
+
+	/* And one copy for every other CPU */
+	for (i = 1; i < NR_CPUS; i++) {
+		memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
+		       newinfo->entries,
+		       SMP_ALIGN(newinfo->size));
+	}
+
+	return ret;
+}
+
+static struct ipt_table_info *
+replace_table(struct ipt_table *table,
+	      unsigned int num_counters,
+	      struct ipt_table_info *newinfo,
+	      int *error)
+{
+	struct ipt_table_info *oldinfo;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+	{
+		struct ipt_entry *table_base;
+		unsigned int i;
+
+		for (i = 0; i < NR_CPUS; i++) {
+			table_base =
+				(void *)newinfo->entries
+				+ TABLE_OFFSET(newinfo, i);
+
+			table_base->comefrom = 0xdead57ac;
+		}
+	}
+#endif
+
+	/* Do the substitution. */
+	write_lock_bh(&table->lock);
+	/* Check inside lock: is the old number correct? */
+	if (num_counters != table->private->number) {
+		duprintf("num_counters != table->private->number (%u/%u)\n",
+			 num_counters, table->private->number);
+		write_unlock_bh(&table->lock);
+		*error = -EAGAIN;
+		return NULL;
+	}
+	oldinfo = table->private;
+	table->private = newinfo;
+	newinfo->initial_entries = oldinfo->initial_entries;
+	write_unlock_bh(&table->lock);
+
+	return oldinfo;
+}
+
+/* Gets counters. */
+static inline int
+add_entry_to_counter(const struct ipt_entry *e,
+		     struct ipt_counters total[],
+		     unsigned int *i)
+{
+	ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+	(*i)++;
+	return 0;
+}
+
+static void
+get_counters(const struct ipt_table_info *t,
+	     struct ipt_counters counters[])
+{
+	unsigned int cpu;
+	unsigned int i;
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		i = 0;
+		IPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+				  t->size,
+				  add_entry_to_counter,
+				  counters,
+				  &i);
+	}
+}
+
+static int
+copy_entries_to_user(unsigned int total_size,
+		     struct ipt_table *table,
+		     void *userptr)
+{
+	unsigned int off, num, countersize;
+	struct ipt_entry *e;
+	struct ipt_counters *counters;
+	int ret = 0;
+
+	/* We need atomic snapshot of counters: rest doesn't change
+	   (other than comefrom, which userspace doesn't care
+	   about). */
+	countersize = sizeof(struct ipt_counters) * table->private->number;
+	counters = vmalloc(countersize);
+
+	if (counters == NULL)
+		return -ENOMEM;
+
+	/* First, sum counters... */
+	memset(counters, 0, countersize);
+	write_lock_bh(&table->lock);
+	get_counters(table->private, counters);
+	write_unlock_bh(&table->lock);
+
+	/* ... then copy entire thing from CPU 0... */
+	if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+		ret = -EFAULT;
+		goto free_counters;
+	}
+
+	/* FIXME: use iterator macros --RR */
+	/* ... then go back and fix counters and names */
+	for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
+		unsigned int i;
+		struct ipt_entry_match *m;
+		struct ipt_entry_target *t;
+
+		e = (struct ipt_entry *)(table->private->entries + off);
+		if (copy_to_user(userptr + off
+				 + offsetof(struct ipt_entry, counters),
+				 &counters[num],
+				 sizeof(counters[num])) != 0) {
+			ret = -EFAULT;
+			goto free_counters;
+		}
+
+		for (i = sizeof(struct ipt_entry);
+		     i < e->target_offset;
+		     i += m->u.match_size) {
+			m = (void *)e + i;
+
+			if (copy_to_user(userptr + off + i
+					 + offsetof(struct ipt_entry_match,
+						    u.user.name),
+					 m->u.kernel.match->name,
+					 strlen(m->u.kernel.match->name)+1)
+			    != 0) {
+				ret = -EFAULT;
+				goto free_counters;
+			}
+		}
+
+		t = ipt_get_target(e);
+		if (copy_to_user(userptr + off + e->target_offset
+				 + offsetof(struct ipt_entry_target,
+					    u.user.name),
+				 t->u.kernel.target->name,
+				 strlen(t->u.kernel.target->name)+1) != 0) {
+			ret = -EFAULT;
+			goto free_counters;
+		}
+	}
+
+ free_counters:
+	vfree(counters);
+	return ret;
+}
+
+static int
+get_entries(const struct ipt_get_entries *entries,
+	    struct ipt_get_entries *uptr)
+{
+	int ret;
+	struct ipt_table *t;
+
+	t = find_table_lock(entries->name, &ret, &ipt_mutex);
+	if (t) {
+		duprintf("t->private->number = %u\n",
+			 t->private->number);
+		if (entries->size == t->private->size)
+			ret = copy_entries_to_user(t->private->size,
+						   t, uptr->entrytable);
+		else {
+			duprintf("get_entries: I've got %u not %u!\n",
+				 t->private->size,
+				 entries->size);
+			ret = -EINVAL;
+		}
+		up(&ipt_mutex);
+	} else
+		duprintf("get_entries: Can't find %s!\n",
+			 entries->name);
+
+	return ret;
+}
+
+static int
+do_replace(void *user, unsigned int len)
+{
+	int ret;
+	struct ipt_replace tmp;
+	struct ipt_table *t;
+	struct ipt_table_info *newinfo, *oldinfo;
+	struct ipt_counters *counters;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	/* Hack: Causes ipchains to give correct error msg --RR */
+	if (len != sizeof(tmp) + tmp.size)
+		return -ENOPROTOOPT;
+
+	/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
+	if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
+		return -ENOMEM;
+
+	newinfo = vmalloc(sizeof(struct ipt_table_info)
+			  + SMP_ALIGN(tmp.size) * NR_CPUS);
+	if (!newinfo)
+		return -ENOMEM;
+
+	if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+			   tmp.size) != 0) {
+		ret = -EFAULT;
+		goto free_newinfo;
+	}
+
+	counters = vmalloc(tmp.num_counters * sizeof(struct ipt_counters));
+	if (!counters) {
+		ret = -ENOMEM;
+		goto free_newinfo;
+	}
+	memset(counters, 0, tmp.num_counters * sizeof(struct ipt_counters));
+
+	ret = translate_table(tmp.name, tmp.valid_hooks,
+			      newinfo, tmp.size, tmp.num_entries,
+			      tmp.hook_entry, tmp.underflow);
+	if (ret != 0)
+		goto free_newinfo_counters;
+
+	duprintf("ip_tables: Translated table\n");
+
+	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	if (!t)
+		goto free_newinfo_counters_untrans;
+
+	/* You lied! */
+	if (tmp.valid_hooks != t->valid_hooks) {
+		duprintf("Valid hook crap: %08X vs %08X\n",
+			 tmp.valid_hooks, t->valid_hooks);
+		ret = -EINVAL;
+		goto free_newinfo_counters_untrans_unlock;
+	}
+
+	oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
+	if (!oldinfo)
+		goto free_newinfo_counters_untrans_unlock;
+
+	/* Update module usage count based on number of rules */
+	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+		oldinfo->number, oldinfo->initial_entries, newinfo->number);
+	if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
+ 	    (newinfo->number > oldinfo->initial_entries))
+		__MOD_INC_USE_COUNT(t->me);
+	else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
+	 	 (newinfo->number <= oldinfo->initial_entries))
+		__MOD_DEC_USE_COUNT(t->me);
+
+	/* Get the old counters. */
+	get_counters(oldinfo, counters);
+	/* Decrease module usage counts and free resource */
+	IPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
+	vfree(oldinfo);
+	/* Silent error: too late now. */
+	copy_to_user(tmp.counters, counters,
+		     sizeof(struct ipt_counters) * tmp.num_counters);
+	vfree(counters);
+	up(&ipt_mutex);
+	return 0;
+
+ free_newinfo_counters_untrans_unlock:
+	up(&ipt_mutex);
+ free_newinfo_counters_untrans:
+	IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
+ free_newinfo_counters:
+	vfree(counters);
+ free_newinfo:
+	vfree(newinfo);
+	return ret;
+}
+
+/* We're lazy, and add to the first CPU; overflow works its fey magic
+ * and everything is OK. */
+static inline int
+add_counter_to_entry(struct ipt_entry *e,
+		     const struct ipt_counters addme[],
+		     unsigned int *i)
+{
+#if 0
+	duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
+		 *i,
+		 (long unsigned int)e->counters.pcnt,
+		 (long unsigned int)e->counters.bcnt,
+		 (long unsigned int)addme[*i].pcnt,
+		 (long unsigned int)addme[*i].bcnt);
+#endif
+
+	ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
+
+	(*i)++;
+	return 0;
+}
+
+static int
+do_add_counters(void *user, unsigned int len)
+{
+	unsigned int i;
+	struct ipt_counters_info tmp, *paddc;
+	struct ipt_table *t;
+	int ret;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ipt_counters))
+		return -EINVAL;
+
+	paddc = vmalloc(len);
+	if (!paddc)
+		return -ENOMEM;
+
+	if (copy_from_user(paddc, user, len) != 0) {
+		ret = -EFAULT;
+		goto free;
+	}
+
+	t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+	if (!t)
+		goto free;
+
+	write_lock_bh(&t->lock);
+	if (t->private->number != paddc->num_counters) {
+		ret = -EINVAL;
+		goto unlock_up_free;
+	}
+
+	i = 0;
+	IPT_ENTRY_ITERATE(t->private->entries,
+			  t->private->size,
+			  add_counter_to_entry,
+			  paddc->counters,
+			  &i);
+ unlock_up_free:
+	write_unlock_bh(&t->lock);
+	up(&ipt_mutex);
+ free:
+	vfree(paddc);
+
+	return ret;
+}
+
+static int
+do_ipt_set_ctl(struct sock *sk,	int cmd, void *user, unsigned int len)
+{
+	int ret;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch (cmd) {
+	case IPT_SO_SET_REPLACE:
+		ret = do_replace(user, len);
+		break;
+
+	case IPT_SO_SET_ADD_COUNTERS:
+		ret = do_add_counters(user, len);
+		break;
+
+	default:
+		duprintf("do_ipt_set_ctl:  unknown request %i\n", cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int
+do_ipt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+	int ret;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch (cmd) {
+	case IPT_SO_GET_INFO: {
+		char name[IPT_TABLE_MAXNAMELEN];
+		struct ipt_table *t;
+
+		if (*len != sizeof(struct ipt_getinfo)) {
+			duprintf("length %u != %u\n", *len,
+				 sizeof(struct ipt_getinfo));
+			ret = -EINVAL;
+			break;
+		}
+
+		if (copy_from_user(name, user, sizeof(name)) != 0) {
+			ret = -EFAULT;
+			break;
+		}
+		name[IPT_TABLE_MAXNAMELEN-1] = '\0';
+		t = find_table_lock(name, &ret, &ipt_mutex);
+		if (t) {
+			struct ipt_getinfo info;
+
+			info.valid_hooks = t->valid_hooks;
+			memcpy(info.hook_entry, t->private->hook_entry,
+			       sizeof(info.hook_entry));
+			memcpy(info.underflow, t->private->underflow,
+			       sizeof(info.underflow));
+			info.num_entries = t->private->number;
+			info.size = t->private->size;
+			strcpy(info.name, name);
+
+			if (copy_to_user(user, &info, *len) != 0)
+				ret = -EFAULT;
+			else
+				ret = 0;
+
+			up(&ipt_mutex);
+		}
+	}
+	break;
+
+	case IPT_SO_GET_ENTRIES: {
+		struct ipt_get_entries get;
+
+		if (*len < sizeof(get)) {
+			duprintf("get_entries: %u < %u\n", *len, sizeof(get));
+			ret = -EINVAL;
+		} else if (copy_from_user(&get, user, sizeof(get)) != 0) {
+			ret = -EFAULT;
+		} else if (*len != sizeof(struct ipt_get_entries) + get.size) {
+			duprintf("get_entries: %u != %u\n", *len,
+				 sizeof(struct ipt_get_entries) + get.size);
+			ret = -EINVAL;
+		} else
+			ret = get_entries(&get, user);
+		break;
+	}
+
+	default:
+		duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/* Registration hooks for targets. */
+int
+ipt_register_target(struct ipt_target *target)
+{
+	int ret;
+
+	MOD_INC_USE_COUNT;
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	if (!list_named_insert(&ipt_target, target)) {
+		duprintf("ipt_register_target: `%s' already in list!\n",
+			 target->name);
+		ret = -EINVAL;
+		MOD_DEC_USE_COUNT;
+	}
+	up(&ipt_mutex);
+	return ret;
+}
+
+void
+ipt_unregister_target(struct ipt_target *target)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_target, target);
+	up(&ipt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int
+ipt_register_match(struct ipt_match *match)
+{
+	int ret;
+
+	MOD_INC_USE_COUNT;
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	if (!list_named_insert(&ipt_match, match)) {
+		duprintf("ipt_register_match: `%s' already in list!\n",
+			 match->name);
+		MOD_DEC_USE_COUNT;
+		ret = -EINVAL;
+	}
+	up(&ipt_mutex);
+
+	return ret;
+}
+
+void
+ipt_unregister_match(struct ipt_match *match)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_match, match);
+	up(&ipt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int ipt_register_table(struct ipt_table *table)
+{
+	int ret;
+	struct ipt_table_info *newinfo;
+	static struct ipt_table_info bootstrap
+		= { 0, 0, 0, { 0 }, { 0 }, { } };
+
+	MOD_INC_USE_COUNT;
+	newinfo = vmalloc(sizeof(struct ipt_table_info)
+			  + SMP_ALIGN(table->table->size) * NR_CPUS);
+	if (!newinfo) {
+		ret = -ENOMEM;
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+	memcpy(newinfo->entries, table->table->entries, table->table->size);
+
+	ret = translate_table(table->name, table->valid_hooks,
+			      newinfo, table->table->size,
+			      table->table->num_entries,
+			      table->table->hook_entry,
+			      table->table->underflow);
+	if (ret != 0) {
+		vfree(newinfo);
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+
+	ret = down_interruptible(&ipt_mutex);
+	if (ret != 0) {
+		vfree(newinfo);
+		MOD_DEC_USE_COUNT;
+		return ret;
+	}
+
+	/* Don't autoload: we'd eat our tail... */
+	if (list_named_find(&ipt_tables, table->name)) {
+		ret = -EEXIST;
+		goto free_unlock;
+	}
+
+	/* Simplifies replace_table code. */
+	table->private = &bootstrap;
+	if (!replace_table(table, 0, newinfo, &ret))
+		goto free_unlock;
+
+	duprintf("table->private->number = %u\n",
+		 table->private->number);
+	
+	/* save number of initial entries */
+	table->private->initial_entries = table->private->number;
+
+	table->lock = RW_LOCK_UNLOCKED;
+	list_prepend(&ipt_tables, table);
+
+ unlock:
+	up(&ipt_mutex);
+	return ret;
+
+ free_unlock:
+	vfree(newinfo);
+	MOD_DEC_USE_COUNT;
+	goto unlock;
+}
+
+void ipt_unregister_table(struct ipt_table *table)
+{
+	down(&ipt_mutex);
+	LIST_DELETE(&ipt_tables, table);
+	up(&ipt_mutex);
+
+	/* Decrease module usage counts and free resources */
+	IPT_ENTRY_ITERATE(table->private->entries, table->private->size,
+			  cleanup_entry, NULL);
+	vfree(table->private);
+	MOD_DEC_USE_COUNT;
+}
+
+/* Returns 1 if the port is matched by the range, 0 otherwise */
+static inline int
+port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
+{
+	int ret;
+
+	ret = (port >= min && port <= max) ^ invert;
+	return ret;
+}
+
+static int
+tcp_find_option(u_int8_t option,
+		const struct tcphdr *tcp,
+		u_int16_t datalen,
+		int invert,
+		int *hotdrop)
+{
+	unsigned int i = sizeof(struct tcphdr);
+	const u_int8_t *opt = (u_int8_t *)tcp;
+
+	duprintf("tcp_match: finding option\n");
+	/* If we don't have the whole header, drop packet. */
+	if (tcp->doff * 4 > datalen) {
+		*hotdrop = 1;
+		return 0;
+	}
+
+	while (i < tcp->doff * 4) {
+		if (opt[i] == option) return !invert;
+		if (opt[i] < 2) i++;
+		else i += opt[i+1]?:1;
+	}
+
+	return invert;
+}
+
+static int
+tcp_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)
+{
+	const struct tcphdr *tcp = hdr;
+	const struct ipt_tcp *tcpinfo = matchinfo;
+
+	/* To quote Alan:
+
+	   Don't allow a fragment of TCP 8 bytes in. Nobody normal
+	   causes this. Its a cracker trying to break in by doing a
+	   flag overwrite to pass the direction checks.
+	*/
+
+	if (offset == 1) {
+		duprintf("Dropping evil TCP offset=1 frag.\n");
+		*hotdrop = 1;
+		return 0;
+	} else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil TCP offset=0 tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* FIXME: Try tcp doff >> packet len against various stacks --RR */
+
+#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+
+	/* Must not be a fragment. */
+	return !offset
+		&& port_match(tcpinfo->spts[0], tcpinfo->spts[1],
+			      ntohs(tcp->source),
+			      !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT))
+		&& port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
+			      ntohs(tcp->dest),
+			      !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT))
+		&& FWINVTCP((((unsigned char *)tcp)[13]
+			     & tcpinfo->flg_mask)
+			    == tcpinfo->flg_cmp,
+			    IPT_TCP_INV_FLAGS)
+		&& (!tcpinfo->option
+		    || tcp_find_option(tcpinfo->option, tcp, datalen,
+				       tcpinfo->invflags
+				       & IPT_TCP_INV_OPTION,
+				       hotdrop));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+tcp_checkentry(const char *tablename,
+	       const struct ipt_ip *ip,
+	       void *matchinfo,
+	       unsigned int matchsize,
+	       unsigned int hook_mask)
+{
+	const struct ipt_tcp *tcpinfo = matchinfo;
+
+	/* Must specify proto == TCP, and no unknown invflags */
+	return ip->proto == IPPROTO_TCP
+		&& !(ip->invflags & IPT_INV_PROTO)
+		&& matchsize == IPT_ALIGN(sizeof(struct ipt_tcp))
+		&& !(tcpinfo->invflags & ~IPT_TCP_INV_MASK);
+}
+
+static int
+udp_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)
+{
+	const struct udphdr *udp = hdr;
+	const struct ipt_udp *udpinfo = matchinfo;
+
+	if (offset == 0 && datalen < sizeof(struct udphdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil UDP tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& port_match(udpinfo->spts[0], udpinfo->spts[1],
+			      ntohs(udp->source),
+			      !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
+		&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
+			      ntohs(udp->dest),
+			      !!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+udp_checkentry(const char *tablename,
+	       const struct ipt_ip *ip,
+	       void *matchinfo,
+	       unsigned int matchinfosize,
+	       unsigned int hook_mask)
+{
+	const struct ipt_udp *udpinfo = matchinfo;
+
+	/* Must specify proto == UDP, and no unknown invflags */
+	if (ip->proto != IPPROTO_UDP || (ip->invflags & IPT_INV_PROTO)) {
+		duprintf("ipt_udp: Protocol %u != %u\n", ip->proto,
+			 IPPROTO_UDP);
+		return 0;
+	}
+	if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_udp))) {
+		duprintf("ipt_udp: matchsize %u != %u\n",
+			 matchinfosize, IPT_ALIGN(sizeof(struct ipt_udp)));
+		return 0;
+	}
+	if (udpinfo->invflags & ~IPT_UDP_INV_MASK) {
+		duprintf("ipt_udp: unknown flags %X\n",
+			 udpinfo->invflags);
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Returns 1 if the type and code is matched by the range, 0 otherwise */
+static inline int
+icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
+		     u_int8_t type, u_int8_t code,
+		     int invert)
+{
+	return (type == test_type && code >= min_code && code <= max_code)
+		^ invert;
+}
+
+static int
+icmp_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)
+{
+	const struct icmphdr *icmp = hdr;
+	const struct ipt_icmp *icmpinfo = matchinfo;
+
+	if (offset == 0 && datalen < 2) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil ICMP tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& icmp_type_code_match(icmpinfo->type,
+					icmpinfo->code[0],
+					icmpinfo->code[1],
+					icmp->type, icmp->code,
+					!!(icmpinfo->invflags&IPT_ICMP_INV));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+icmp_checkentry(const char *tablename,
+	   const struct ipt_ip *ip,
+	   void *matchinfo,
+	   unsigned int matchsize,
+	   unsigned int hook_mask)
+{
+	const struct ipt_icmp *icmpinfo = matchinfo;
+
+	/* Must specify proto == ICMP, and no unknown invflags */
+	return ip->proto == IPPROTO_ICMP
+		&& !(ip->invflags & IPT_INV_PROTO)
+		&& matchsize == IPT_ALIGN(sizeof(struct ipt_icmp))
+		&& !(icmpinfo->invflags & ~IPT_ICMP_INV);
+}
+
+/* The built-in targets: standard (NULL) and error. */
+static struct ipt_target ipt_standard_target
+= { { NULL, NULL }, IPT_STANDARD_TARGET, NULL, NULL, NULL };
+static struct ipt_target ipt_error_target
+= { { NULL, NULL }, IPT_ERROR_TARGET, ipt_error, NULL, NULL };
+
+static struct nf_sockopt_ops ipt_sockopts
+= { { NULL, NULL }, PF_INET, IPT_BASE_CTL, IPT_SO_SET_MAX+1, do_ipt_set_ctl,
+    IPT_BASE_CTL, IPT_SO_GET_MAX+1, do_ipt_get_ctl, 0, NULL  };
+
+static struct ipt_match tcp_matchstruct
+= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
+static struct ipt_match udp_matchstruct
+= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
+static struct ipt_match icmp_matchstruct
+= { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL };
+
+#ifdef CONFIG_PROC_FS
+static inline int print_name(const struct ipt_table *t,
+			     off_t start_offset, char *buffer, int length,
+			     off_t *pos, unsigned int *count)
+{
+	if ((*count)++ >= start_offset) {
+		unsigned int namelen;
+
+		namelen = sprintf(buffer + *pos, "%s\n", t->name);
+		if (*pos + namelen > length) {
+			/* Stop iterating */
+			return 1;
+		}
+		*pos += namelen;
+	}
+	return 0;
+}
+
+static int ipt_get_tables(char *buffer, char **start, off_t offset, int length)
+{
+	off_t pos = 0;
+	unsigned int count = 0;
+
+	if (down_interruptible(&ipt_mutex) != 0)
+		return 0;
+
+	LIST_FIND(&ipt_tables, print_name, struct ipt_table *,
+		  offset, buffer, length, &pos, &count);
+
+	up(&ipt_mutex);
+
+	/* `start' hack - see fs/proc/generic.c line ~105 */
+	*start=(char *)((unsigned long)count-offset);
+	return pos;
+}
+#endif /*CONFIG_PROC_FS*/
+
+static int __init init(void)
+{
+	int ret;
+
+	/* Noone else will be downing sem now, so we won't sleep */
+	down(&ipt_mutex);
+	list_append(&ipt_target, &ipt_standard_target);
+	list_append(&ipt_target, &ipt_error_target);
+	list_append(&ipt_match, &tcp_matchstruct);
+	list_append(&ipt_match, &udp_matchstruct);
+	list_append(&ipt_match, &icmp_matchstruct);
+	up(&ipt_mutex);
+
+	/* Register setsockopt */
+	ret = nf_register_sockopt(&ipt_sockopts);
+	if (ret < 0) {
+		duprintf("Unable to register sockopts.\n");
+		return ret;
+	}
+
+#ifdef CONFIG_PROC_FS
+	{
+	struct proc_dir_entry *proc;
+
+	proc = proc_net_create("ip_tables_names", 0, ipt_get_tables);
+	if (!proc) {
+		nf_unregister_sockopt(&ipt_sockopts);
+		return -ENOMEM;
+	}
+	proc->owner = THIS_MODULE;
+	}
+#endif
+
+	printk("ip_tables: (C) 2000-2002 Netfilter core team\n");
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	nf_unregister_sockopt(&ipt_sockopts);
+#ifdef CONFIG_PROC_FS
+	proc_net_remove("ip_tables_names");
+#endif
+}
+
+EXPORT_SYMBOL(ipt_register_table);
+EXPORT_SYMBOL(ipt_unregister_table);
+EXPORT_SYMBOL(ipt_register_match);
+EXPORT_SYMBOL(ipt_unregister_match);
+EXPORT_SYMBOL(ipt_do_table);
+EXPORT_SYMBOL(ipt_register_target);
+EXPORT_SYMBOL(ipt_unregister_target);
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/linux2.5/net/ipv4/netfilter/ipt_LOG.c b/br-nf-bds/linux2.5/net/ipv4/netfilter/ipt_LOG.c
new file mode 100644
index 0000000..07bc0e4
--- /dev/null
+++ b/br-nf-bds/linux2.5/net/ipv4/netfilter/ipt_LOG.c
@@ -0,0 +1,368 @@
+/*
+ * This is a module which is used for logging packets.
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/spinlock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+struct in_device;
+#include <net/route.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct esphdr {
+	__u32   spi;
+}; /* FIXME evil kludge */
+        
+/* Use lock to serialize, so printks don't overlap */
+static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+
+/* One level of recursion won't kill us */
+static void dump_packet(const struct ipt_log_info *info,
+			struct iphdr *iph, unsigned int len, int recurse)
+{
+	void *protoh = (u_int32_t *)iph + iph->ihl;
+	unsigned int datalen = len - iph->ihl * 4;
+
+	/* Important fields:
+	 * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+	/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+	printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
+	       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+
+	/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+	printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+	       ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
+	       iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
+
+	/* Max length: 6 "CE DF MF " */
+	if (ntohs(iph->frag_off) & IP_CE)
+		printk("CE ");
+	if (ntohs(iph->frag_off) & IP_DF)
+		printk("DF ");
+	if (ntohs(iph->frag_off) & IP_MF)
+		printk("MF ");
+
+	/* Max length: 11 "FRAG:65535 " */
+	if (ntohs(iph->frag_off) & IP_OFFSET)
+		printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
+
+	if ((info->logflags & IPT_LOG_IPOPT)
+	    && iph->ihl * 4 != sizeof(struct iphdr)) {
+		unsigned int i;
+
+		/* Max length: 127 "OPT (" 15*4*2chars ") " */
+		printk("OPT (");
+		for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
+			printk("%02X", ((u_int8_t *)iph)[i]);
+		printk(") ");
+	}
+
+	switch (iph->protocol) {
+	case IPPROTO_TCP: {
+		struct tcphdr *tcph = protoh;
+
+		/* Max length: 10 "PROTO=TCP " */
+		printk("PROTO=TCP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*tcph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 20 "SPT=65535 DPT=65535 " */
+		printk("SPT=%u DPT=%u ",
+		       ntohs(tcph->source), ntohs(tcph->dest));
+		/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+		if (info->logflags & IPT_LOG_TCPSEQ)
+			printk("SEQ=%u ACK=%u ",
+			       ntohl(tcph->seq), ntohl(tcph->ack_seq));
+		/* Max length: 13 "WINDOW=65535 " */
+		printk("WINDOW=%u ", ntohs(tcph->window));
+		/* Max length: 9 "RES=0x3F " */
+		printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
+		/* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
+		if (tcph->cwr)
+			printk("CWR ");
+		if (tcph->ece)
+			printk("ECE ");
+		if (tcph->urg)
+			printk("URG ");
+		if (tcph->ack)
+			printk("ACK ");
+		if (tcph->psh)
+			printk("PSH ");
+		if (tcph->rst)
+			printk("RST ");
+		if (tcph->syn)
+			printk("SYN ");
+		if (tcph->fin)
+			printk("FIN ");
+		/* Max length: 11 "URGP=65535 " */
+		printk("URGP=%u ", ntohs(tcph->urg_ptr));
+
+		if ((info->logflags & IPT_LOG_TCPOPT)
+		    && tcph->doff * 4 != sizeof(struct tcphdr)) {
+			unsigned int i;
+
+			/* Max length: 127 "OPT (" 15*4*2chars ") " */
+			printk("OPT (");
+			for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
+				printk("%02X", ((u_int8_t *)tcph)[i]);
+			printk(") ");
+		}
+		break;
+	}
+	case IPPROTO_UDP: {
+		struct udphdr *udph = protoh;
+
+		/* Max length: 10 "PROTO=UDP " */
+		printk("PROTO=UDP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*udph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 20 "SPT=65535 DPT=65535 " */
+		printk("SPT=%u DPT=%u LEN=%u ",
+		       ntohs(udph->source), ntohs(udph->dest),
+		       ntohs(udph->len));
+		break;
+	}
+	case IPPROTO_ICMP: {
+		struct icmphdr *icmph = protoh;
+		static size_t required_len[NR_ICMP_TYPES+1]
+			= { [ICMP_ECHOREPLY] = 4,
+			    [ICMP_DEST_UNREACH]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_SOURCE_QUENCH]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_REDIRECT]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_ECHO] = 4,
+			    [ICMP_TIME_EXCEEDED]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_PARAMETERPROB]
+			    = 8 + sizeof(struct iphdr) + 8,
+			    [ICMP_TIMESTAMP] = 20,
+			    [ICMP_TIMESTAMPREPLY] = 20,
+			    [ICMP_ADDRESS] = 12,
+			    [ICMP_ADDRESSREPLY] = 12 };
+
+		/* Max length: 11 "PROTO=ICMP " */
+		printk("PROTO=ICMP ");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < 4) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Max length: 18 "TYPE=255 CODE=255 " */
+		printk("TYPE=%u CODE=%u ", icmph->type, icmph->code);
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (icmph->type <= NR_ICMP_TYPES
+		    && required_len[icmph->type]
+		    && datalen < required_len[icmph->type]) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		switch (icmph->type) {
+		case ICMP_ECHOREPLY:
+		case ICMP_ECHO:
+			/* Max length: 19 "ID=65535 SEQ=65535 " */
+			printk("ID=%u SEQ=%u ",
+			       ntohs(icmph->un.echo.id),
+			       ntohs(icmph->un.echo.sequence));
+			break;
+
+		case ICMP_PARAMETERPROB:
+			/* Max length: 14 "PARAMETER=255 " */
+			printk("PARAMETER=%u ",
+			       ntohl(icmph->un.gateway) >> 24);
+			break;
+		case ICMP_REDIRECT:
+			/* Max length: 24 "GATEWAY=255.255.255.255 " */
+			printk("GATEWAY=%u.%u.%u.%u ", NIPQUAD(icmph->un.gateway));
+			/* Fall through */
+		case ICMP_DEST_UNREACH:
+		case ICMP_SOURCE_QUENCH:
+		case ICMP_TIME_EXCEEDED:
+			/* Max length: 3+maxlen */
+			if (recurse) {
+				printk("[");
+				dump_packet(info,
+					    (struct iphdr *)(icmph + 1),
+					    datalen-sizeof(struct icmphdr),
+					    0);
+				printk("] ");
+			}
+
+			/* Max length: 10 "MTU=65535 " */
+			if (icmph->type == ICMP_DEST_UNREACH
+			    && icmph->code == ICMP_FRAG_NEEDED)
+				printk("MTU=%u ", ntohs(icmph->un.frag.mtu));
+		}
+		break;
+	}
+	/* Max Length */
+	case IPPROTO_AH:
+	case IPPROTO_ESP: {
+		struct esphdr *esph = protoh;
+		int esp= (iph->protocol==IPPROTO_ESP);
+
+		/* Max length: 10 "PROTO=ESP " */
+		printk("PROTO=%s ",esp? "ESP" : "AH");
+
+		if (ntohs(iph->frag_off) & IP_OFFSET)
+			break;
+
+		/* Max length: 25 "INCOMPLETE [65535 bytes] " */
+		if (datalen < sizeof (*esph)) {
+			printk("INCOMPLETE [%u bytes] ", datalen);
+			break;
+		}
+
+		/* Length: 15 "SPI=0xF1234567 " */
+		printk("SPI=0x%x ", ntohl(esph->spi) );
+		break;
+	}
+	/* Max length: 10 "PROTO 255 " */
+	default:
+		printk("PROTO=%u ", iph->protocol);
+	}
+
+	/* Proto    Max log string length */
+	/* IP:      40+46+6+11+127 = 230 */
+	/* TCP:     10+max(25,20+30+13+9+32+11+127) = 252 */
+	/* UDP:     10+max(25,20) = 35 */
+	/* ICMP:    11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
+	/* ESP:     10+max(25)+15 = 50 */
+	/* AH:      9+max(25)+15 = 49 */
+	/* unknown: 10 */
+
+	/* (ICMP allows recursion one level deep) */
+	/* maxlen =  IP + ICMP +  IP + max(TCP,UDP,ICMP,unknown) */
+	/* maxlen = 230+   91  + 230 + 252 = 803 */
+}
+
+static unsigned int
+ipt_log_target(struct sk_buff **pskb,
+	       unsigned int hooknum,
+	       const struct net_device *in,
+	       const struct net_device *out,
+	       const void *targinfo,
+	       void *userinfo)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	const struct ipt_log_info *loginfo = targinfo;
+	char level_string[4] = "< >";
+
+	level_string[1] = '0' + (loginfo->level % 8);
+	spin_lock_bh(&log_lock);
+	printk(level_string);
+	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
+	if ((*pskb)->nf_bridge) {
+		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
+		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
+
+		if (physindev && in != physindev)
+			printk("PHYSIN=%s ", physindev->name);
+		printk("OUT=%s ", out ? out->name : "");
+		if (physoutdev && out != physoutdev)
+			printk("PHYSOUT=%s ", physoutdev->name);
+	}
+
+	if (in && !out) {
+		/* MAC logging for input chain only. */
+		printk("MAC=");
+		if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)iph) {
+			int i;
+			unsigned char *p = (*pskb)->mac.raw;
+			for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++)
+				printk("%02x%c", *p,
+				       i==(*pskb)->dev->hard_header_len - 1
+				       ? ' ':':');
+		} else
+			printk(" ");
+	}
+
+	dump_packet(loginfo, iph, (*pskb)->len, 1);
+	printk("\n");
+	spin_unlock_bh(&log_lock);
+
+	return IPT_CONTINUE;
+}
+
+static int ipt_log_checkentry(const char *tablename,
+			      const struct ipt_entry *e,
+			      void *targinfo,
+			      unsigned int targinfosize,
+			      unsigned int hook_mask)
+{
+	const struct ipt_log_info *loginfo = targinfo;
+
+	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_log_info))) {
+		DEBUGP("LOG: targinfosize %u != %u\n",
+		       targinfosize, IPT_ALIGN(sizeof(struct ipt_log_info)));
+		return 0;
+	}
+
+	if (loginfo->level >= 8) {
+		DEBUGP("LOG: level %u >= 8\n", loginfo->level);
+		return 0;
+	}
+
+	if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
+		DEBUGP("LOG: prefix term %i\n",
+		       loginfo->prefix[sizeof(loginfo->prefix)-1]);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_target ipt_log_reg
+= { { NULL, NULL }, "LOG", ipt_log_target, ipt_log_checkentry, NULL, 
+    THIS_MODULE };
+
+static int __init init(void)
+{
+	if (ipt_register_target(&ipt_log_reg))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	ipt_unregister_target(&ipt_log_reg);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/br-nf-bds/patches/bridge-nf-0.0.10-against-2.4.20.diff b/br-nf-bds/patches/bridge-nf-0.0.10-against-2.4.20.diff
new file mode 100644
index 0000000..874b6c8
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.10-against-2.4.20.diff
@@ -0,0 +1,1191 @@
+bridge-nf-0.0.10-against-2.4.20 - 07 December 2002
+
+--- linux-2.4.20/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.20-patch/include/linux/netfilter.h	Sat Dec  7 00:40:11 2002
+@@ -78,7 +78,7 @@
+ {
+ 	/* The ops struct which sent us to userspace. */
+ 	struct nf_hook_ops *elem;
+-	
++
+ 	/* If we're sent to userspace, this keeps housekeeping info */
+ 	int pf;
+ 	unsigned int hook;
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.20/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.20-patch/include/linux/netfilter_ipv4.h	Sat Dec  7 00:40:11 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.20/include/linux/netfilter_bridge.h	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge.h	Sat Dec  7 00:45:43 2002
+@@ -6,6 +6,9 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -22,14 +25,44 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++
+ enum nf_br_hook_priorities {
+ 	NF_BR_PRI_FIRST = INT_MIN,
+-	NF_BR_PRI_FILTER_BRIDGED = -200,
+-	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
+ 	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_SRC = 300,
+ 	NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
++
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
++
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.20/include/linux/skbuff.h	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/include/linux/skbuff.h	Fri Dec  6 23:41:31 2002
+@@ -92,6 +92,17 @@
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -204,6 +215,9 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1144,6 +1158,20 @@
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.20/net/bridge/br.c	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/net/bridge/br.c	Fri Dec  6 23:42:45 2002
+@@ -45,6 +45,10 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -63,6 +67,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+--- linux-2.4.20/net/bridge/br_forward.c	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/net/bridge/br_forward.c	Fri Dec  6 23:45:03 2002
+@@ -30,18 +30,22 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +57,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +68,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.20/net/bridge/br_input.c	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/net/bridge/br_input.c	Fri Dec  6 23:45:52 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.4.20/net/bridge/br_private.h	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/net/bridge/br_private.h	Sat Dec  7 00:41:40 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -177,6 +180,10 @@
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.20/net/bridge/Makefile	Sat Dec  7 00:49:06 2002
++++ linux-2.4.20-patch/net/bridge/Makefile	Sat Dec  7 00:12:20 2002
+@@ -13,6 +13,11 @@
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.20/net/core/netfilter.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/core/netfilter.c	Fri Dec  6 23:54:19 2002
+@@ -1,4 +1,4 @@
+-/* netfilter.c: look after the filters for various protocols. 
++/* netfilter.c: look after the filters for various protocols.
+  * Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
+  *
+  * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -401,7 +406,7 @@
+ }
+ 
+ /* 
+- * Any packet that leaves via this function must come back 
++ * Any packet that leaves via this function must come back
+  * through nf_reinject().
+  */
+ static void nf_queue(struct sk_buff *skb, 
+@@ -413,6 +418,10 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -528,9 +551,9 @@
+ 
+ 	if (verdict == NF_ACCEPT) {
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+-				     &skb, info->hook, 
++				     &skb, info->hook,
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.20/net/core/skbuff.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/core/skbuff.c	Fri Dec  6 23:57:10 2002
+@@ -245,6 +245,9 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -325,6 +328,9 @@
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -391,6 +397,9 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -403,6 +412,9 @@
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -437,6 +449,10 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.20/net/ipv4/netfilter/ip_tables.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.20-patch/net/ipv4/netfilter/ip_tables.c	Sat Dec  7 01:11:58 2002
+@@ -122,12 +122,19 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -157,7 +164,15 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -170,7 +185,15 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -269,6 +292,9 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -278,6 +304,13 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -313,7 +346,15 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.20/net/ipv4/ip_output.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.20-patch/net/ipv4/ip_output.c	Fri Dec  6 23:59:53 2002
+@@ -879,6 +879,10 @@
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.20/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-patch/net/ipv4/netfilter/ipt_LOG.c	Sat Dec  7 00:01:00 2002
+@@ -289,6 +289,18 @@
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/br_netfilter.c	Sat Dec  7 00:35:01 2002
+@@ -0,0 +1,614 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
++ * functions, and give them back later, when we have determined the real
++ * output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < NUMHOOKS; i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = NUMHOOKS - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff
new file mode 100644
index 0000000..9b21335
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff
@@ -0,0 +1,1086 @@
+bridge-nf-0.0.10-dev-pre1-against-2.5.42 - 13 October
+
+--- linux-2.5.42/include/linux/netfilter.h	Sat Oct 12 06:22:08 2002
++++ linux-2.5.42-brnf/include/linux/netfilter.h	Sun Oct 13 11:45:19 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.5.42/include/linux/netfilter_ipv4.h	Sat Oct 12 06:22:18 2002
++++ linux-2.5.42-brnf/include/linux/netfilter_ipv4.h	Sun Oct 13 11:45:19 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.5.42/include/linux/netfilter_bridge.h	Sat Oct 12 06:22:09 2002
++++ linux-2.5.42-brnf/include/linux/netfilter_bridge.h	Sun Oct 13 11:45:19 2002
+@@ -22,14 +22,27 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
++/* Masks for skb->brnfmask  */
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_COPY_HEADER		0x04
++#define BRNF_DONT_TAKE_PARENT		0x08
++
+ enum nf_br_hook_priorities {
+ 	NF_BR_PRI_FIRST = INT_MIN,
+-	NF_BR_PRI_FILTER_BRIDGED = -200,
+-	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
+ 	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_SRC = 300,
+ 	NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
++/* Used in br_netfilter.c */
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
+ #endif
+--- linux-2.5.42/include/linux/skbuff.h	Sat Oct 12 06:22:09 2002
++++ linux-2.5.42-brnf/include/linux/skbuff.h	Sun Oct 13 11:45:19 2002
+@@ -140,6 +140,8 @@
+  *	@sk: Socket we are owned by
+  *	@stamp: Time we arrived
+  *	@dev: Device we arrived on/are leaving by
++ *	@physindev: Physical device we arrived on - see br_netfilter.c
++ *	@physoutdev: Phsical device we will leave by - see br_netfilter.c
+  *	@h: Transport layer header
+  *	@nh: Network layer header
+  *	@mac: Link layer header
+@@ -166,6 +168,7 @@
+  *	@nfcache: Cache info
+  *	@nfct: Associated connection, if any
+  *	@nf_debug: Netfilter debugging
++ *	@brnfmask: Info about a bridged frame - see br_netfilter.c
+  *	@tc_index: Traffic control index
+  */
+ 
+@@ -178,6 +181,8 @@
+ 	struct sock		*sk;
+ 	struct timeval		stamp;
+ 	struct net_device	*dev;
++	struct net_device	*physindev;
++	struct net_device	*physoutdev;
+ 
+ 	union {
+ 		struct tcphdr	*th;
+@@ -236,6 +241,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int		nf_debug;
+ #endif
++	unsigned int		brnfmask;
+ #endif /* CONFIG_NETFILTER */
+ #if defined(CONFIG_HIPPI)
+ 	union {
+--- linux-2.5.42/net/bridge/br.c	Sat Oct 12 06:21:34 2002
++++ linux-2.5.42-brnf/net/bridge/br.c	Sun Oct 13 11:45:19 2002
+@@ -45,6 +45,8 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++	if (br_netfilter_init())
++		return 1;
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -63,6 +65,7 @@
+ 
+ static void __exit br_deinit(void)
+ {
++	br_netfilter_fini();
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+--- linux-2.5.42/net/bridge/br_forward.c	Sat Oct 12 06:21:37 2002
++++ linux-2.5.42-brnf/net/bridge/br_forward.c	Sun Oct 13 11:45:19 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +53,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +64,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.5.42/net/bridge/br_input.c	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/bridge/br_input.c	Sun Oct 13 11:45:19 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.5.42/net/bridge/br_private.h	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/bridge/br_private.h	Sun Oct 13 11:45:19 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.5.42/net/bridge/Makefile	Sat Oct 12 06:22:45 2002
++++ linux-2.5.42-brnf/net/bridge/Makefile	Sun Oct 13 11:45:19 2002
+@@ -9,6 +9,11 @@
+ bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++bridge-objs	+= br_netfilter.o
++endif
++
+ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.5.42/net/core/netfilter.c	Sat Oct 12 06:22:07 2002
++++ linux-2.5.42-brnf/net/core/netfilter.c	Sun Oct 13 11:45:19 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +442,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +461,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +494,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +543,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.5.42/net/core/skbuff.c	Sat Oct 12 06:21:34 2002
++++ linux-2.5.42-brnf/net/core/skbuff.c	Sun Oct 13 11:45:19 2002
+@@ -234,6 +234,8 @@
+ 	skb->sk		  = NULL;
+ 	skb->stamp.tv_sec = 0;	/* No idea about time */
+ 	skb->dev	  = NULL;
++	skb->physindev	  = NULL;
++	skb->physoutdev	  = NULL;
+ 	skb->dst	  = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type	  = PACKET_HOST;	/* Default type */
+@@ -248,6 +250,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug	  = 0;
+ #endif
++	skb->brnfmask	  = 0;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index	  = 0;
+@@ -363,6 +366,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -392,6 +397,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++	C(brnfmask);
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -418,6 +424,8 @@
+ 	new->list	= NULL;
+ 	new->sk		= NULL;
+ 	new->dev	= old->dev;
++	new->physindev	= old->physindev;
++	new->physoutdev	= old->physoutdev;
+ 	new->priority	= old->priority;
+ 	new->protocol	= old->protocol;
+ 	new->dst	= dst_clone(old->dst);
+@@ -438,6 +446,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug	= old->nf_debug;
+ #endif
++	new->brnfmask	= old->brnfmask;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index	= old->tc_index;
+--- linux-2.5.42/net/ipv4/ip_output.c	Sat Oct 12 06:22:45 2002
++++ linux-2.5.42-brnf/net/ipv4/ip_output.c	Sun Oct 13 11:45:19 2002
+@@ -75,6 +75,7 @@
+ #include <net/inetpeer.h>
+ #include <linux/igmp.h>
+ #include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge.h>
+ #include <linux/mroute.h>
+ #include <linux/netlink.h>
+ 
+@@ -908,6 +909,18 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++
++		/*
++		 *	Fragments with a bridge device destination need
++		 *	to get the Ethernet header copied here, as
++		 *	br_dev_queue_push_xmit() can't do this.
++		 *	See net/bridge/br_netfilter.c
++		 */
++
++#ifdef CONFIG_NETFILTER
++		if (skb->brnfmask & BRNF_COPY_HEADER)
++			memcpy(skb2->data - 16, skb->data - 16, 16);
++#endif
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.5.42/net/ipv4/netfilter/ip_tables.c	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ip_tables.c	Sun Oct 13 11:45:19 2002
+@@ -121,12 +121,14 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+-	unsigned long ret;
++	unsigned long ret, ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +158,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +177,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +282,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +292,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -311,7 +329,8 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.5.42/net/ipv4/netfilter/ipt_LOG.c	Sat Oct 12 06:21:38 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ipt_LOG.c	Sun Oct 13 11:45:19 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.42-brnf/net/bridge/br_netfilter.c	Sun Oct 13 11:45:19 2002
+@@ -0,0 +1,602 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff,v 1.1 2002/10/19 10:46:51 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while ip_route_output() will return success. The source
++ * address for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (skb->brnfmask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		skb->brnfmask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				skb->brnfmask |= BRNF_BRIDGED_DNAT;
++				skb->dev = skb->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->brnfmask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		skb->brnfmask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
++ * functions, and give them back later, when we have determined the real
++ * output device. This is done in here.
++ *
++ * If (skb->brnfmask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if skb->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * skb->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++
++	realindev = skb->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (skb->brnfmask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (skb->brnfmask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			skb->brnfmask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((skb->brnfmask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	/* Fragmented packets need a good Ethernet header, tell this to
++	 * ip_output.c::ip_fragment().
++	 */
++	skb->brnfmask |= BRNF_COPY_HEADER;
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL) {
++			skb->brnfmask &= BRNF_DONT_TAKE_PARENT;
++			skb->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < NUMHOOKS; i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = NUMHOOKS - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2-against-2.5.42.diff b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2-against-2.5.42.diff
new file mode 100644
index 0000000..bd56344
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2-against-2.5.42.diff
@@ -0,0 +1,1132 @@
+bridge-nf-0.0.10-dev-pre2-against-2.5.42 - 20 October
+
+--- linux-2.5.42/include/linux/netfilter.h	Sat Oct 12 06:22:08 2002
++++ linux-2.5.42-brnf/include/linux/netfilter.h	Sun Oct 20 15:33:11 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.5.42/include/linux/netfilter_ipv4.h	Sat Oct 12 06:22:18 2002
++++ linux-2.5.42-brnf/include/linux/netfilter_ipv4.h	Sun Oct 20 15:33:11 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.5.42/include/linux/netfilter_bridge.h	Sat Oct 12 06:22:09 2002
++++ linux-2.5.42-brnf/include/linux/netfilter_bridge.h	Sun Oct 20 21:20:53 2002
+@@ -6,6 +6,7 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#include <asm/atomic.h>
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -22,14 +23,39 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++
+ enum nf_br_hook_priorities {
+ 	NF_BR_PRI_FIRST = INT_MIN,
+-	NF_BR_PRI_FILTER_BRIDGED = -200,
+-	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
+ 	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_SRC = 300,
+ 	NF_BR_PRI_LAST = INT_MAX,
++};
++
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
++
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
+ };
+ 
+ #endif
+--- linux-2.5.42/include/linux/skbuff.h	Sat Oct 12 06:22:09 2002
++++ linux-2.5.42-brnf/include/linux/skbuff.h	Sun Oct 20 15:33:11 2002
+@@ -96,6 +96,14 @@
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
+ #endif
+ 
+ struct sk_buff_head {
+@@ -166,6 +174,7 @@
+  *	@nfcache: Cache info
+  *	@nfct: Associated connection, if any
+  *	@nf_debug: Netfilter debugging
++ *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
+  *	@tc_index: Traffic control index
+  */
+ 
+@@ -236,6 +245,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int		nf_debug;
+ #endif
++	struct nf_bridge_info	*nf_bridge;
+ #endif /* CONFIG_NETFILTER */
+ #if defined(CONFIG_HIPPI)
+ 	union {
+@@ -1136,6 +1146,17 @@
+ {
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
++}
++
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
+ }
+ #endif
+ 
+--- linux-2.5.42/net/bridge/br.c	Sat Oct 12 06:21:34 2002
++++ linux-2.5.42-brnf/net/bridge/br.c	Sun Oct 20 21:24:42 2002
+@@ -45,6 +45,10 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -63,6 +67,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+--- linux-2.5.42/net/bridge/br_forward.c	Sat Oct 12 06:21:37 2002
++++ linux-2.5.42-brnf/net/bridge/br_forward.c	Sun Oct 20 21:22:52 2002
+@@ -30,18 +30,23 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
++
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +58,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +69,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.5.42/net/bridge/br_input.c	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/bridge/br_input.c	Sun Oct 20 21:23:29 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.5.42/net/bridge/br_private.h	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/bridge/br_private.h	Sun Oct 20 15:33:11 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.5.42/net/bridge/Makefile	Sat Oct 12 06:22:45 2002
++++ linux-2.5.42-brnf/net/bridge/Makefile	Sun Oct 20 15:33:11 2002
+@@ -9,6 +9,11 @@
+ bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++bridge-objs	+= br_netfilter.o
++endif
++
+ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.5.42/net/core/netfilter.c	Sat Oct 12 06:22:07 2002
++++ linux-2.5.42-brnf/net/core/netfilter.c	Sun Oct 20 15:33:11 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +442,20 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +465,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +498,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +547,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.5.42/net/core/skbuff.c	Sat Oct 12 06:21:34 2002
++++ linux-2.5.42-brnf/net/core/skbuff.c	Sun Oct 20 15:33:11 2002
+@@ -248,6 +248,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug	  = 0;
+ #endif
++	skb->nf_bridge	  = NULL;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index	  = 0;
+@@ -327,6 +328,7 @@
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++	nf_bridge_put(skb->nf_bridge);
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -392,6 +394,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++	C(nf_bridge);
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -404,6 +407,7 @@
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++	nf_bridge_get(skb->nf_bridge);
+ #endif
+ 	return n;
+ }
+@@ -438,6 +442,8 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug	= old->nf_debug;
+ #endif
++	new->nf_bridge	= old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index	= old->tc_index;
+--- linux-2.5.42/net/ipv4/ip_output.c	Sat Oct 12 06:22:45 2002
++++ linux-2.5.42-brnf/net/ipv4/ip_output.c	Sun Oct 20 16:15:47 2002
+@@ -894,6 +894,8 @@
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.5.42/net/ipv4/netfilter/ip_tables.c	Sat Oct 12 06:21:35 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ip_tables.c	Sun Oct 20 15:33:11 2002
+@@ -121,12 +121,14 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+-	unsigned long ret;
++	unsigned long ret, ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +158,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +177,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +282,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +292,16 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	if ((*pskb)->nf_bridge) {
++		physindev = (*pskb)->nf_bridge->physindev ?
++			(*pskb)->nf_bridge->physindev->name : nulldevname;
++		physoutdev = (*pskb)->nf_bridge->physoutdev ?
++			(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++	} else {
++		physindev = nulldevname;
++		physoutdev = nulldevname;
++	}
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -311,7 +336,8 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.5.42/net/ipv4/netfilter/ipt_LOG.c	Sat Oct 12 06:21:38 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ipt_LOG.c	Sun Oct 20 15:33:11 2002
+@@ -285,10 +285,18 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		printk("OUT=%s ", out ? out->name : "");
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.42-brnf/net/bridge/br_netfilter.c	Sun Oct 20 21:37:15 2002
+@@ -0,0 +1,616 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.10-dev-pre2-against-2.5.42.diff,v 1.2 2002/10/20 19:46:33 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while ip_route_output() will return success. The source
++ * address for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
++ * functions, and give them back later, when we have determined the real
++ * output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < NUMHOOKS; i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = NUMHOOKS - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2.001-against-2.5.42.diff b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2.001-against-2.5.42.diff
new file mode 100644
index 0000000..4d296d7
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.10-dev-pre2.001-against-2.5.42.diff
@@ -0,0 +1,583 @@
+bridge-nf-0.0.10-dev-pre2.001-against-2.5.42 - 20 October
+
+--- linux-2.5.42/include/linux/netfilter_bridge.h	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/include/linux/netfilter_bridge.h	Sun Oct 20 21:20:53 2002
+@@ -6,6 +6,7 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#include <asm/atomic.h>
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -22,11 +23,9 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
+-/* Masks for skb->brnfmask  */
+ #define BRNF_PKT_TYPE			0x01
+ #define BRNF_BRIDGED_DNAT		0x02
+-#define BRNF_COPY_HEADER		0x04
+-#define BRNF_DONT_TAKE_PARENT		0x08
++#define BRNF_DONT_TAKE_PARENT		0x04
+ 
+ enum nf_br_hook_priorities {
+ 	NF_BR_PRI_FIRST = INT_MIN,
+@@ -39,10 +38,24 @@
+ 	NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
+-/* Used in br_netfilter.c */
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
++
+ struct bridge_skb_cb {
+ 	union {
+ 		__u32 ipv4;
+ 	} daddr;
+ };
++
+ #endif
+--- linux-2.5.42/include/linux/skbuff.h	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/include/linux/skbuff.h	Sun Oct 20 15:33:11 2002
+@@ -96,6 +96,14 @@
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
+ #endif
+ 
+ struct sk_buff_head {
+@@ -140,8 +148,6 @@
+  *	@sk: Socket we are owned by
+  *	@stamp: Time we arrived
+  *	@dev: Device we arrived on/are leaving by
+- *	@physindev: Physical device we arrived on - see br_netfilter.c
+- *	@physoutdev: Phsical device we will leave by - see br_netfilter.c
+  *	@h: Transport layer header
+  *	@nh: Network layer header
+  *	@mac: Link layer header
+@@ -168,7 +174,7 @@
+  *	@nfcache: Cache info
+  *	@nfct: Associated connection, if any
+  *	@nf_debug: Netfilter debugging
+- *	@brnfmask: Info about a bridged frame - see br_netfilter.c
++ *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
+  *	@tc_index: Traffic control index
+  */
+ 
+@@ -181,8 +187,6 @@
+ 	struct sock		*sk;
+ 	struct timeval		stamp;
+ 	struct net_device	*dev;
+-	struct net_device	*physindev;
+-	struct net_device	*physoutdev;
+ 
+ 	union {
+ 		struct tcphdr	*th;
+@@ -241,7 +245,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int		nf_debug;
+ #endif
+-	unsigned int		brnfmask;
++	struct nf_bridge_info	*nf_bridge;
+ #endif /* CONFIG_NETFILTER */
+ #if defined(CONFIG_HIPPI)
+ 	union {
+@@ -1142,6 +1146,17 @@
+ {
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
++}
++
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
+ }
+ #endif
+ 
+--- linux-2.5.42/net/bridge/br.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/bridge/br.c	Sun Oct 20 21:24:42 2002
+@@ -45,8 +45,10 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
+ 	if (br_netfilter_init())
+ 		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -65,7 +67,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
+ 	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+--- linux-2.5.42/net/bridge/br_forward.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/bridge/br_forward.c	Sun Oct 20 21:22:52 2002
+@@ -32,7 +32,12 @@
+ 
+ int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
++
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+--- linux-2.5.42/net/core/netfilter.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/core/netfilter.c	Sun Oct 20 15:33:11 2002
+@@ -418,8 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
+-	struct net_device *physindev;
+-	struct net_device *physoutdev;
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -442,8 +442,12 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
+-	if ((physindev = skb->physindev)) dev_hold(physindev);
+-	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
+ 
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+--- linux-2.5.42/net/core/skbuff.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/core/skbuff.c	Sun Oct 20 15:33:11 2002
+@@ -234,8 +234,6 @@
+ 	skb->sk		  = NULL;
+ 	skb->stamp.tv_sec = 0;	/* No idea about time */
+ 	skb->dev	  = NULL;
+-	skb->physindev	  = NULL;
+-	skb->physoutdev	  = NULL;
+ 	skb->dst	  = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type	  = PACKET_HOST;	/* Default type */
+@@ -250,7 +248,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug	  = 0;
+ #endif
+-	skb->brnfmask	  = 0;
++	skb->nf_bridge	  = NULL;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index	  = 0;
+@@ -330,6 +328,7 @@
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++	nf_bridge_put(skb->nf_bridge);
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -366,8 +365,6 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
+-	C(physindev);
+-	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -397,7 +394,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
+-	C(brnfmask);
++	C(nf_bridge);
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -410,6 +407,7 @@
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++	nf_bridge_get(skb->nf_bridge);
+ #endif
+ 	return n;
+ }
+@@ -424,8 +422,6 @@
+ 	new->list	= NULL;
+ 	new->sk		= NULL;
+ 	new->dev	= old->dev;
+-	new->physindev	= old->physindev;
+-	new->physoutdev	= old->physoutdev;
+ 	new->priority	= old->priority;
+ 	new->protocol	= old->protocol;
+ 	new->dst	= dst_clone(old->dst);
+@@ -446,7 +442,8 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug	= old->nf_debug;
+ #endif
+-	new->brnfmask	= old->brnfmask;
++	new->nf_bridge	= old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index	= old->tc_index;
+--- linux-2.5.42/net/ipv4/ip_output.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/ipv4/ip_output.c	Sun Oct 20 16:15:47 2002
+@@ -75,7 +75,6 @@
+ #include <net/inetpeer.h>
+ #include <linux/igmp.h>
+ #include <linux/netfilter_ipv4.h>
+-#include <linux/netfilter_bridge.h>
+ #include <linux/mroute.h>
+ #include <linux/netlink.h>
+ 
+@@ -895,6 +894,8 @@
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+@@ -909,18 +910,6 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
+-
+-		/*
+-		 *	Fragments with a bridge device destination need
+-		 *	to get the Ethernet header copied here, as
+-		 *	br_dev_queue_push_xmit() can't do this.
+-		 *	See net/bridge/br_netfilter.c
+-		 */
+-
+-#ifdef CONFIG_NETFILTER
+-		if (skb->brnfmask & BRNF_COPY_HEADER)
+-			memcpy(skb2->data - 16, skb->data - 16, 16);
+-#endif
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.5.42/net/ipv4/netfilter/ip_tables.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ip_tables.c	Sun Oct 20 15:33:11 2002
+@@ -292,8 +292,15 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
+-	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
+-	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++	if ((*pskb)->nf_bridge) {
++		physindev = (*pskb)->nf_bridge->physindev ?
++			(*pskb)->nf_bridge->physindev->name : nulldevname;
++		physoutdev = (*pskb)->nf_bridge->physoutdev ?
++			(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++	} else {
++		physindev = nulldevname;
++		physoutdev = nulldevname;
++	}
+ 
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+--- linux-2.5.42/net/ipv4/netfilter/ipt_LOG.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/ipv4/netfilter/ipt_LOG.c	Sun Oct 20 15:33:11 2002
+@@ -286,11 +286,16 @@
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+ 	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
+-	if ((*pskb)->physindev && in != (*pskb)->physindev)
+-		printk("PHYSIN=%s ", (*pskb)->physindev->name);
+-	printk("OUT=%s ", out ? out->name : "");
+-	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
+-		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		printk("OUT=%s ", out ? out->name : "");
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
+ 
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+--- linux-2.5.42/net/bridge/br_netfilter.c	Sun Oct 20 21:27:40 2002
++++ linux-2.5.42-brnf/net/bridge/br_netfilter.c	Sun Oct 20 21:37:15 2002
+@@ -133,7 +133,7 @@
+ 
+ 	if (skb->pkt_type == PACKET_OTHERHOST) {
+ 		skb->pkt_type = PACKET_HOST;
+-		skb->brnfmask |= BRNF_PKT_TYPE;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
+ 	}
+ 
+ 	skb->dev = bridge_parent(skb->dev);
+@@ -145,14 +145,15 @@
+ {
+ 	struct net_device *dev = skb->dev;
+ 	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ 
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+ #endif
+ 
+-	if (skb->brnfmask & BRNF_PKT_TYPE) {
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ 		skb->pkt_type = PACKET_OTHERHOST;
+-		skb->brnfmask ^= BRNF_PKT_TYPE;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
+ 	}
+ 
+ 	if (dnat_took_place(skb)) {
+@@ -179,8 +180,8 @@
+ 				/* Tell br_nf_local_out this is a
+ 				 * bridged frame
+ 				 */
+-				skb->brnfmask |= BRNF_BRIDGED_DNAT;
+-				skb->dev = skb->physindev;
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
+ 				clear_cb(skb);
+ 				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
+ 					       skb, skb->dev, NULL,
+@@ -197,7 +198,7 @@
+ 	}
+ 
+ 	clear_cb(skb);
+-	skb->dev = skb->physindev;
++	skb->dev = nf_bridge->physindev;
+ 	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 		       br_handle_frame_finish, 1);
+ 
+@@ -217,6 +218,7 @@
+ 	struct iphdr *iph;
+ 	__u32 len;
+ 	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
+ 
+ 	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+ 		return NF_ACCEPT;
+@@ -251,13 +253,15 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+ #endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
+ 
+ 	if (skb->pkt_type == PACKET_OTHERHOST) {
+ 		skb->pkt_type = PACKET_HOST;
+-		skb->brnfmask |= BRNF_PKT_TYPE;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
+ 	}
+ 
+-	skb->physindev = skb->dev;
++	nf_bridge->physindev = skb->dev;
+ 	skb->dev = bridge_parent(skb->dev);
+ 	store_orig_dstaddr(skb);
+ 
+@@ -302,16 +306,18 @@
+ /* PF_BRIDGE/FORWARD *************************************************/
+ static int br_nf_forward_finish(struct sk_buff *skb)
+ {
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug ^= (1 << NF_BR_FORWARD);
+ #endif
+ 
+-	if (skb->brnfmask & BRNF_PKT_TYPE) {
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ 		skb->pkt_type = PACKET_OTHERHOST;
+-		skb->brnfmask ^= BRNF_PKT_TYPE;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
+ 	}
+ 
+-	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
+ 			skb->dev, br_forward_finish, 1);
+ 
+ 	return 0;
+@@ -330,6 +336,7 @@
+    int (*okfn)(struct sk_buff *))
+ {
+ 	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
+ 
+ 	if (skb->protocol != __constant_htons(ETH_P_IP))
+ 		return NF_ACCEPT;
+@@ -338,14 +345,15 @@
+ 	skb->nf_debug ^= (1 << NF_BR_FORWARD);
+ #endif
+ 
++	nf_bridge = skb->nf_bridge;
+ 	if (skb->pkt_type == PACKET_OTHERHOST) {
+ 		skb->pkt_type = PACKET_HOST;
+-		skb->brnfmask |= BRNF_PKT_TYPE;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
+ 	}
+ 
+-	skb->physoutdev = skb->dev;
++	nf_bridge->physoutdev = skb->dev;
+ 
+-	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
+ 			bridge_parent(skb->dev), br_nf_forward_finish);
+ 
+ 	return NF_STOLEN;
+@@ -375,18 +383,18 @@
+  * functions, and give them back later, when we have determined the real
+  * output device. This is done in here.
+  *
+- * If (skb->brnfmask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
+  * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
+  * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
+  * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
+  * will be executed.
+- * Otherwise, if skb->physindev is NULL, the bridge-nf code never touched
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
+  * this packet before, and so the packet was locally originated. We fake
+  * the PF_INET/LOCAL_OUT hook.
+- * Finally, if skb->physindev isn't NULL, then the packet was IP routed,
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
+  * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
+  * even routed packets that didn't arrive on a bridge interface have their
+- * skb->physindev set.
++ * nf_bridge->physindev set.
+  */
+ 
+ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
+@@ -396,6 +404,7 @@
+ 	int (*okfn)(struct sk_buff *skb);
+ 	struct net_device *realindev;
+ 	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
+ 
+ 	if (skb->protocol != __constant_htons(ETH_P_IP))
+ 		return NF_ACCEPT;
+@@ -406,19 +415,20 @@
+ 	if (skb->dst == NULL)
+ 		return NF_ACCEPT;
+ 
+-	skb->physoutdev = skb->dev;
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
+ 
+-	realindev = skb->physindev;
++	realindev = nf_bridge->physindev;
+ 
+ 	/* Bridged, take PF_BRIDGE/FORWARD.
+ 	 * (see big note in front of br_nf_pre_routing_finish)
+ 	 */
+-	if (skb->brnfmask & BRNF_BRIDGED_DNAT) {
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
+ 		okfn = br_forward_finish;
+ 
+-		if (skb->brnfmask & BRNF_PKT_TYPE) {
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ 			skb->pkt_type = PACKET_OTHERHOST;
+-			skb->brnfmask ^= BRNF_PKT_TYPE;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
+ 		}
+ 
+ 		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
+@@ -429,7 +439,7 @@
+ 		 * generated traffic hasn't.
+ 		 */
+ 		if (realindev != NULL) {
+-			if (((skb->brnfmask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
+ 			    has_bridge_parent(realindev))
+ 				realindev = bridge_parent(realindev);
+ 
+@@ -457,6 +467,7 @@
+    int (*okfn)(struct sk_buff *))
+ {
+ 	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
+ 
+ 	/* Be very paranoid.  */
+ 	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+@@ -489,13 +500,10 @@
+ 	 */
+ 	if (skb->pkt_type == PACKET_OTHERHOST) {
+ 		skb->pkt_type = PACKET_HOST;
+-		skb->brnfmask |= BRNF_PKT_TYPE;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
+ 	}
+ 
+-	/* Fragmented packets need a good Ethernet header, tell this to
+-	 * ip_output.c::ip_fragment().
+-	 */
+-	skb->brnfmask |= BRNF_COPY_HEADER;
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
+ 
+ 	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+ 		bridge_parent(skb->dev), br_dev_queue_push_xmit);
+@@ -535,6 +543,12 @@
+ 	    okfn != br_nf_local_out_finish &&
+ 	    okfn != br_dev_queue_push_xmit) {
+ 		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
+ 
+ 		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
+ 		 * will need the indev then. For a brouter, the real indev
+@@ -542,9 +556,9 @@
+ 		 * doesn't use the bridge parent of the indev by using
+ 		 * the BRNF_DONT_TAKE_PARENT mask.
+ 		 */
+-		if (hook == NF_IP_FORWARD && skb->physindev == NULL) {
+-			skb->brnfmask &= BRNF_DONT_TAKE_PARENT;
+-			skb->physindev = (struct net_device *)in;
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
+ 		}
+ 		okfn(skb);
+ 		return NF_STOLEN;
diff --git a/br-nf-bds/patches/bridge-nf-0.0.10-pre1-against-2.4.20-pre7.diff b/br-nf-bds/patches/bridge-nf-0.0.10-pre1-against-2.4.20-pre7.diff
new file mode 100644
index 0000000..ead9670
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.10-pre1-against-2.4.20-pre7.diff
@@ -0,0 +1,1069 @@
+bridge-nf-0.0.10-pre1-against-2.4.20-pre7 - 8 October
+
+--- linux-2.4.20-pre7-ebt/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.20-pre7/include/linux/netfilter.h	Wed Oct  9 20:51:21 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.20-pre7-ebt/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.20-pre7/include/linux/netfilter_ipv4.h	Wed Oct  9 21:14:19 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge.h	Wed Oct  9 21:32:43 2002
++++ linux-2.4.20-pre7/include/linux/netfilter_bridge.h	Wed Oct  9 20:57:44 2002
+@@ -22,14 +22,27 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
++/* Masks for skb->brnfmask  */
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_COPY_HEADER		0x04
++#define BRNF_DONT_TAKE_PARENT		0x08
++
+ enum nf_br_hook_priorities {
+ 	NF_BR_PRI_FIRST = INT_MIN,
+-	NF_BR_PRI_FILTER_BRIDGED = -200,
+-	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
+ 	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
+ 	NF_BR_PRI_NAT_SRC = 300,
+ 	NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
++/* Used in br_netfilter.c */
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
+ #endif
+--- linux-2.4.20-pre7-ebt/include/linux/skbuff.h	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7/include/linux/skbuff.h	Wed Oct  9 20:27:43 2002
+@@ -135,6 +135,8 @@
+ 	struct sock	*sk;			/* Socket we are owned by 			*/
+ 	struct timeval	stamp;			/* Time we arrived				*/
+ 	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
++	struct net_device	*physindev;     /* Physical device we arrived on                */
++	struct net_device	*physoutdev;    /* Physical device we will leave by             */
+ 
+ 	/* Transport layer header */
+ 	union
+@@ -204,6 +206,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++	unsigned int		brnfmask; /* Info about a bridged frame - see br_netfilter.c    */
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+--- linux-2.4.20-pre7-ebt/net/bridge/br.c	Wed Oct  9 21:32:42 2002
++++ linux-2.4.20-pre7/net/bridge/br.c	Wed Oct  9 19:38:18 2002
+@@ -44,6 +44,8 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++	if (br_netfilter_init())
++		return 1;
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -67,6 +69,7 @@
+ 
+ static void __exit br_deinit(void)
+ {
++	br_netfilter_fini();
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.4.20-pre7-ebt/net/bridge/br_forward.c	Wed Oct  9 21:32:42 2002
++++ linux-2.4.20-pre7/net/bridge/br_forward.c	Wed Oct  9 19:38:18 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +53,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +64,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.20-pre7-ebt/net/bridge/br_input.c	Wed Oct  9 21:32:42 2002
++++ linux-2.4.20-pre7/net/bridge/br_input.c	Wed Oct  9 19:38:18 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.4.20-pre7-ebt/net/bridge/br_private.h	Wed Oct  9 21:32:42 2002
++++ linux-2.4.20-pre7/net/bridge/br_private.h	Wed Oct  9 21:12:04 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.4.20-pre7-ebt/net/bridge/Makefile	Wed Oct  9 21:32:42 2002
++++ linux-2.4.20-pre7/net/bridge/Makefile	Wed Oct  9 20:22:56 2002
+@@ -13,6 +13,11 @@
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.20-pre7-ebt/net/core/netfilter.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7/net/core/netfilter.c	Wed Oct  9 19:38:18 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +442,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +461,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +494,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +543,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.20-pre7-ebt/net/core/skbuff.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7/net/core/skbuff.c	Wed Oct  9 20:07:13 2002
+@@ -231,6 +231,8 @@
+ 	skb->sk = NULL;
+ 	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	skb->dev = NULL;
++	skb->physindev = NULL;
++	skb->physoutdev = NULL;
+ 	skb->dst = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type = PACKET_HOST;	/* Default type */
+@@ -245,6 +247,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++	skb->brnfmask = 0;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -362,6 +365,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -391,6 +396,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++	C(brnfmask);
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -417,6 +423,8 @@
+ 	new->list=NULL;
+ 	new->sk=NULL;
+ 	new->dev=old->dev;
++	new->physindev=old->physindev;
++	new->physoutdev=old->physoutdev;
+ 	new->priority=old->priority;
+ 	new->protocol=old->protocol;
+ 	new->dst=dst_clone(old->dst);
+@@ -437,6 +445,7 @@
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++	new->brnfmask=old->brnfmask;
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.20-pre7-ebt/net/ipv4/ip_output.c	Wed Oct  9 21:32:23 2002
++++ linux-2.4.20-pre7/net/ipv4/ip_output.c	Wed Oct  9 20:53:26 2002
+@@ -75,6 +75,7 @@
+ #include <net/inetpeer.h>
+ #include <linux/igmp.h>
+ #include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge.h>
+ #include <linux/mroute.h>
+ #include <linux/netlink.h>
+ 
+@@ -893,6 +896,18 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++
++		/*
++		 *	Fragments with a bridge device destination need
++		 *	to get the Ethernet header copied here, as
++		 *	br_dev_queue_push_xmit() can't do this.
++		 *	See net/bridge/br_netfilter.c
++		 */
++
++#ifdef CONFIG_NETFILTER
++		if (skb->brnfmask & BRNF_COPY_HEADER)
++			memcpy(skb2->data - 16, skb->data - 16, 16);
++#endif
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.4.20-pre7-ebt/net/ipv4/netfilter/ip_tables.c	Wed Oct  9 21:32:23 2002
++++ linux-2.4.20-pre7/net/ipv4/netfilter/ip_tables.c	Wed Oct  9 19:38:18 2002
+@@ -122,12 +122,14 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+-	unsigned long ret;
++	unsigned long ret, ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -157,7 +159,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -170,7 +178,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -269,6 +283,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -278,6 +293,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -313,7 +331,8 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.20-pre7-ebt/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre7/net/ipv4/netfilter/ipt_LOG.c	Wed Oct  9 19:38:18 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7/net/bridge/br_netfilter.c	Wed Oct  9 21:22:12 2002
+@@ -0,0 +1,602 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.10-pre1-against-2.4.20-pre7.diff,v 1.1 2002/10/19 10:46:51 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while ip_route_output() will return success. The source
++ * address for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (skb->brnfmask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		skb->brnfmask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				skb->brnfmask |= BRNF_BRIDGED_DNAT;
++				skb->dev = skb->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->brnfmask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		skb->brnfmask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
++ * functions, and give them back later, when we have determined the real
++ * output device. This is done in here.
++ *
++ * If (skb->brnfmask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if skb->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * skb->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++
++	realindev = skb->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (skb->brnfmask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (skb->brnfmask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			skb->brnfmask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((skb->brnfmask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->brnfmask |= BRNF_PKT_TYPE;
++	}
++
++	/* Fragmented packets need a good Ethernet header, tell this to
++	 * ip_output.c::ip_fragment().
++	 */
++	skb->brnfmask |= BRNF_COPY_HEADER;
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL) {
++			skb->brnfmask &= BRNF_DONT_TAKE_PARENT;
++			skb->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < NUMHOOKS; i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = NUMHOOKS - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.7-bds-against-2.4.18.diff b/br-nf-bds/patches/bridge-nf-0.0.7-bds-against-2.4.18.diff
new file mode 100644
index 0000000..eb33426
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.7-bds-against-2.4.18.diff
@@ -0,0 +1,975 @@
+All patches are the same as Lennert's 0.0.6 except the last patch
+(br_netfilter.c).
+
+--- linux-2.4.18/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.18-brnf0.0.6/include/linux/netfilter.h	Tue Feb 26 04:53:26 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++		nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.18/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.18-brnf0.0.6/include/linux/netfilter_ipv4.h	Tue Feb 26 04:53:57 2002
+@@ -54,6 +54,7 @@
+ 	NF_IP_PRI_CONNTRACK = -200,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.18/include/linux/skbuff.h	Thu Nov 22 20:46:26 2001
++++ linux-2.4.18-brnf0.0.6/include/linux/skbuff.h	Tue Feb 26 04:53:47 2002
+@@ -135,6 +135,8 @@
+ 	struct sock	*sk;			/* Socket we are owned by 			*/
+ 	struct timeval	stamp;			/* Time we arrived				*/
+ 	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
++	struct net_device	*physindev;	/* Physical device we arrived on		*/
++	struct net_device	*physoutdev;	/* Physical device we will leave by		*/
+ 
+ 	/* Transport layer header */
+ 	union
+--- linux-2.4.18/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/bridge/br.c	Tue Feb 26 04:54:47 2002
+@@ -42,6 +42,11 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if (br_netfilter_init())
++		return 1;
++#endif
++
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -65,6 +70,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_BRIDGE_NF
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.4.18/net/bridge/br_forward.c	Wed Aug 15 10:54:35 2001
++++ linux-2.4.18-brnf0.0.6/net/bridge/br_forward.c	Tue Feb 26 04:54:15 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -54,7 +54,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -65,7 +65,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.18/net/bridge/br_input.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/bridge/br_input.c	Tue Feb 26 04:54:24 2002
+@@ -46,7 +46,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.4.18/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/bridge/br_private.h	Tue Feb 26 04:54:47 2002
+@@ -120,6 +120,7 @@
+ extern void br_inc_use_count(void);
+ 
+ /* br_device.c */
++extern int  br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+ extern void br_dev_setup(struct net_device *dev);
+ extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+ 
+@@ -144,8 +145,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int  br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int  br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +169,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int  br_handle_frame_finish(struct sk_buff *skb);
+ extern void br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -177,6 +181,10 @@
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.18/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.18-brnf0.0.6/net/bridge/Makefile	Tue Feb 26 04:54:47 2002
+@@ -13,4 +13,6 @@
+ 			br_stp_if.o br_stp_timer.o
+ obj-m		:= $(O_TARGET)
+ 
++obj-$(CONFIG_BRIDGE_NF) += br_netfilter.o
++
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.18/net/Config.in	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/Config.in	Tue Feb 26 04:54:47 2002
+@@ -61,6 +61,9 @@
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++      bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
++   fi
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+    bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+--- linux-2.4.18/net/core/netfilter.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/core/netfilter.c	Tue Feb 26 04:53:47 2002
+@@ -343,10 +343,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -414,6 +419,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -436,11 +443,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -450,7 +462,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -482,7 +495,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -531,7 +544,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.18/net/core/skbuff.c	Fri Dec 21 18:42:05 2001
++++ linux-2.4.18-brnf0.0.6/net/core/skbuff.c	Tue Feb 26 04:53:47 2002
+@@ -231,6 +231,8 @@
+ 	skb->sk = NULL;
+ 	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	skb->dev = NULL;
++	skb->physindev = NULL;
++	skb->physoutdev = NULL;
+ 	skb->dst = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type = PACKET_HOST;	/* Default type */
+@@ -362,6 +364,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -417,6 +421,8 @@
+ 	new->list=NULL;
+ 	new->sk=NULL;
+ 	new->dev=old->dev;
++	new->physindev=old->physindev;
++	new->physoutdev=old->physoutdev;
+ 	new->priority=old->priority;
+ 	new->protocol=old->protocol;
+ 	new->dst=dst_clone(old->dst);
+--- linux-2.4.18/net/ipv4/ip_output.c	Tue Feb 26 04:52:58 2002
++++ linux-2.4.18-brnf0.0.6/net/ipv4/ip_output.c	Tue Feb 26 04:53:47 2002
+@@ -819,6 +819,8 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -882,6 +884,7 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++		memcpy(skb2->data - 16, skb->data - 16, 16);
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.4.18/net/ipv4/netfilter/ip_tables.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/ipv4/netfilter/ip_tables.c	Tue Feb 26 04:53:52 2002
+@@ -121,12 +121,15 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +159,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +178,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +283,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +293,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +331,7 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev, &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.18/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.18-brnf0.0.6/net/ipv4/netfilter/ipt_LOG.c	Tue Feb 26 04:54:03 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ bridge-nf-0.0.7-bds/net/bridge/br_netfilter.c	Fri Apr 26 22:56:26 2002
+@@ -0,0 +1,565 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.7-bds-against-2.4.18.diff,v 1.1 2002/06/01 19:23:59 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	hookno = NF_IP_LOCAL_OUT;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev),
++			okfn,
++			NF_IP_PRI_BRIDGE_SABOTAGE + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.8-3-bds-vs-2.4.20-pre5.diff b/br-nf-bds/patches/bridge-nf-0.0.8-3-bds-vs-2.4.20-pre5.diff
new file mode 100644
index 0000000..1fd7b3d
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.8-3-bds-vs-2.4.20-pre5.diff
@@ -0,0 +1,1014 @@
+bridge-nf-0.0.8-3-bds vs 2.4.20-pre5 - 30 August
+
+this is a patch against 2.4.20-pre5 that needs to be applied AFTER
+the ebtables patch for 2.4.20-pre5
+
+--- linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/include/linux/netfilter.h	Fri Aug 30 20:55:09 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/include/linux/netfilter_ipv4.h	Fri Aug 30 20:55:09 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.20-pre5-ebt2.0-rc2/include/linux/skbuff.h	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/include/linux/skbuff.h	Fri Aug 30 20:55:09 2002
+@@ -135,6 +135,8 @@
+ 	struct sock	*sk;			/* Socket we are owned by 			*/
+ 	struct timeval	stamp;			/* Time we arrived				*/
+ 	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
++	struct net_device	*physindev;	/* Physical device we arrived on		*/
++	struct net_device	*physoutdev;	/* Physical device we will leave by		*/
+ 
+ 	/* Transport layer header */
+ 	union
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br.c	Fri Aug 30 20:48:58 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/br.c	Fri Aug 30 20:55:09 2002
+@@ -50,6 +50,11 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if (br_netfilter_init())
++		return 1;
++#endif
++
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -73,6 +78,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_BRIDGE_NF
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/br_forward.c	Fri Aug 30 20:55:09 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -50,7 +50,7 @@
+ {
+ 	skb->dev = to->dev;
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +61,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br_input.c	Fri Aug 30 20:48:58 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/br_input.c	Fri Aug 30 20:55:09 2002
+@@ -50,7 +50,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br_private.h	Fri Aug 30 20:48:58 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/br_private.h	Fri Aug 30 20:55:09 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/Makefile	Fri Aug 30 20:48:58 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/Makefile	Fri Aug 30 20:55:09 2002
+@@ -19,4 +19,6 @@
+ 			br_stp_if.o br_stp_timer.o
+ obj-m		:= $(O_TARGET)
+ 
++obj-$(CONFIG_BRIDGE_NF) += br_netfilter.o
++
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/Config.in	Fri Aug 30 20:48:58 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/Config.in	Fri Aug 30 20:55:09 2002
+@@ -69,6 +69,9 @@
+    source net/bridge/netfilter/Config.in
+ fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++      bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
++   fi
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+    bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/core/netfilter.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/core/netfilter.c	Fri Aug 30 20:55:09 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++#ifdef CONFIG_BRIDGE_NF
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,20 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#ifdef CONFIG_BRIDGE_NF
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +467,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +500,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +549,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/core/skbuff.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/core/skbuff.c	Fri Aug 30 20:55:09 2002
+@@ -231,6 +231,10 @@
+ 	skb->sk = NULL;
+ 	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	skb->dev = NULL;
++#ifdef CONFIG_BRIDGE_NF
++	skb->physindev = NULL;
++	skb->physoutdev = NULL;
++#endif
+ 	skb->dst = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type = PACKET_HOST;	/* Default type */
+@@ -362,6 +366,10 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++#ifdef CONFIG_BRIDGE_NF
++	C(physindev);
++	C(physoutdev);
++#endif
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -417,6 +425,10 @@
+ 	new->list=NULL;
+ 	new->sk=NULL;
+ 	new->dev=old->dev;
++#ifdef CONFIG_BRIDGE_NF
++	new->physindev=old->physindev;
++	new->physoutdev=old->physoutdev;
++#endif
+ 	new->priority=old->priority;
+ 	new->protocol=old->protocol;
+ 	new->dst=dst_clone(old->dst);
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/ipv4/ip_output.c	Fri Aug 30 20:48:41 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/ipv4/ip_output.c	Fri Aug 30 20:55:09 2002
+@@ -830,6 +830,10 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++#ifdef CONFIG_BRIDGE_NF
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
++#endif
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -893,6 +897,9 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++#ifdef CONFIG_BRIDGE_NF
++		memcpy(skb2->data - 16, skb->data - 16, 16);
++#endif
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/ipv4/netfilter/ip_tables.c	Fri Aug 30 20:48:42 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/ipv4/netfilter/ip_tables.c	Fri Aug 30 20:55:09 2002
+@@ -122,12 +122,19 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#ifdef CONFIG_BRIDGE_NF
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#ifdef CONFIG_BRIDGE_NF
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -157,7 +164,15 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#ifdef CONFIG_BRIDGE_NF
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -170,7 +185,15 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#ifdef CONFIG_BRIDGE_NF
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -269,6 +292,9 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#ifdef CONFIG_BRIDGE_NF
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -278,6 +304,11 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#ifdef CONFIG_BRIDGE_NF
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -313,7 +344,15 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#ifdef CONFIG_BRIDGE_NF
++		    physindev,
++#endif
++		    outdev,
++#ifdef CONFIG_BRIDGE_NF
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.20-pre5-ebt2.0-rc2/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/ipv4/netfilter/ipt_LOG.c	Fri Aug 30 20:55:09 2002
+@@ -285,10 +285,17 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++#ifdef CONFIG_BRIDGE_NF
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++#endif
++	printk("OUT=%s ", out ? out->name : "");
++#ifdef CONFIG_BRIDGE_NF
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt+br-nf-0.0.3/net/bridge/br_netfilter.c	Fri Aug 30 20:55:09 2002
+@@ -0,0 +1,567 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.8-3-bds-vs-2.4.20-pre5.diff,v 1.1 2002/08/30 19:01:05 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno, prio;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	hookno = NF_IP_LOCAL_OUT;
++	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		// there is an iptables mangle table FORWARD chain with
++		// priority -150. This chain should see the physical out-dev.
++		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev), okfn, prio + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.8-3-dev-bds-vs-2.5.32.diff b/br-nf-bds/patches/bridge-nf-0.0.8-3-dev-bds-vs-2.5.32.diff
new file mode 100644
index 0000000..3c2a742
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.8-3-dev-bds-vs-2.5.32.diff
@@ -0,0 +1,1029 @@
+bridge-nf-0.0.8-3-dev-bds vs 2.5.32 - 29 August
+
+this is a patch against 2.5.32 that needs to be applied AFTER
+the ebtables patch for 2.5.32
+
+--- linux-2.5.32/include/linux/netfilter.h	Tue Aug 27 21:26:43 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/include/linux/netfilter.h	Thu Aug 29 22:08:46 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.5.32/include/linux/netfilter_ipv4.h	Tue Aug 27 21:27:11 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/include/linux/netfilter_ipv4.h	Thu Aug 29 22:08:46 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.5.32/include/linux/skbuff.h	Tue Aug 27 21:26:32 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/include/linux/skbuff.h	Thu Aug 29 22:08:46 2002
+@@ -137,6 +137,8 @@
+  *	@sk: Socket we are owned by
+  *	@stamp: Time we arrived
+  *	@dev: Device we arrived on/are leaving by
++ *	@physindev: Phsical device we arrived on (see bridge-nf)
++ *	@physoutdev: Phsical device we will leave by (see bridge-nf)
+  *	@h: Transport layer header
+  *	@nh: Network layer header
+  *	@mac: Link layer header
+@@ -175,6 +177,10 @@
+ 	struct sock		*sk;
+ 	struct timeval		stamp;
+ 	struct net_device	*dev;
++#ifdef CONFIG_BRIDGE_NF
++	struct net_device	*physindev;
++	struct net_device	*physoutdev;
++#endif
+ 
+ 	union {
+ 		struct tcphdr	*th;
+--- linux-2.5.32/net/bridge/br.c	Thu Aug 29 21:58:05 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/br.c	Thu Aug 29 22:08:46 2002
+@@ -50,6 +50,11 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if (br_netfilter_init())
++		return 1;
++#endif
++
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -73,6 +78,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_BRIDGE_NF
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.5.32/net/bridge/br_forward.c	Tue Aug 27 21:26:39 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/br_forward.c	Thu Aug 29 22:08:46 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -50,7 +50,7 @@
+ {
+ 	skb->dev = to->dev;
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +61,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.5.32/net/bridge/br_input.c	Thu Aug 29 21:58:05 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/br_input.c	Thu Aug 29 22:08:46 2002
+@@ -50,7 +50,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.5.32/net/bridge/br_private.h	Thu Aug 29 21:58:05 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/br_private.h	Thu Aug 29 22:08:46 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.5.32/net/bridge/Makefile	Thu Aug 29 21:58:05 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/Makefile	Thu Aug 29 22:08:46 2002
+@@ -15,4 +15,10 @@
+ 			br_stp_if.o br_stp_timer.o
+ obj-$(CONFIG_BRIDGE_EBT) += netfilter/
+ 
++ifneq ($(CONFIG_BRIDGE_NF),n)
++ifneq ($(CONFIG_BRIDGE_NF),)
++bridge-objs += br_netfilter.o
++endif
++endif
++
+ include $(TOPDIR)/Rules.make
+--- linux-2.5.32/net/Config.in	Thu Aug 29 21:58:05 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/Config.in	Thu Aug 29 22:08:47 2002
+@@ -66,6 +66,9 @@
+    source net/bridge/netfilter/Config.in
+ fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++      bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
++   fi
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+    bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
+--- linux-2.5.32/net/core/netfilter.c	Tue Aug 27 21:26:42 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/core/netfilter.c	Thu Aug 29 22:08:46 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++#ifdef CONFIG_BRIDGE_NF
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,20 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#ifdef CONFIG_BRIDGE_NF
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +467,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +500,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +549,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.5.32/net/core/skbuff.c	Tue Aug 27 21:26:36 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/core/skbuff.c	Thu Aug 29 22:08:46 2002
+@@ -232,6 +232,10 @@
+ 	skb->sk		  = NULL;
+ 	skb->stamp.tv_sec = 0;	/* No idea about time */
+ 	skb->dev	  = NULL;
++#ifdef CONFIG_BRIDGE_NF
++	skb->physindev	  = NULL;
++	skb->physoutdev	  = NULL;
++#endif
+ 	skb->dst	  = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type	  = PACKET_HOST;	/* Default type */
+@@ -361,6 +365,10 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++#ifdef CONFIG_BRIDGE_NF
++	C(physindev);
++	C(physoutdev);
++#endif
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -416,6 +424,10 @@
+ 	new->list	= NULL;
+ 	new->sk		= NULL;
+ 	new->dev	= old->dev;
++#ifdef CONFIG_BRIDGE_NF
++	new->physindev	= old->physindev;
++	new->physoutdev	= old->physoutdev;
++#endif
+ 	new->priority	= old->priority;
+ 	new->protocol	= old->protocol;
+ 	new->dst	= dst_clone(old->dst);
+--- linux-2.5.32/net/ipv4/ip_output.c	Tue Aug 27 21:27:33 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/ipv4/ip_output.c	Thu Aug 29 22:08:47 2002
+@@ -835,6 +835,10 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++#ifdef CONFIG_BRIDGE_NF
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
++#endif
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -898,6 +902,9 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++#ifdef CONFIG_BRIDGE_NF
++		memcpy(skb2->data - 16, skb->data - 16, 16);
++#endif
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.5.32/net/ipv4/netfilter/ip_tables.c	Tue Aug 27 21:26:37 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/ipv4/netfilter/ip_tables.c	Thu Aug 29 22:08:46 2002
+@@ -122,12 +122,19 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#ifdef CONFIG_BRIDGE_NF
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#ifdef CONFIG_BRIDGE_NF
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -157,7 +164,15 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#ifdef CONFIG_BRIDGE_NF
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -170,7 +185,15 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#ifdef CONFIG_BRIDGE_NF
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -269,6 +292,9 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#ifdef CONFIG_BRIDGE_NF
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -278,6 +304,11 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#ifdef CONFIG_BRIDGE_NF
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +343,15 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#ifdef CONFIG_BRIDGE_NF
++		    physindev,
++#endif
++		    outdev,
++#ifdef CONFIG_BRIDGE_NF
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.5.32/net/ipv4/netfilter/ipt_LOG.c	Tue Aug 27 21:26:39 2002
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/ipv4/netfilter/ipt_LOG.c	Thu Aug 29 22:08:46 2002
+@@ -285,10 +285,17 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++#ifdef CONFIG_BRIDGE_NF
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++#endif
++	printk("OUT=%s ", out ? out->name : "");
++#ifdef CONFIG_BRIDGE_NF
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt+br-nf-0.0.3/net/bridge/br_netfilter.c	Thu Aug 29 22:08:46 2002
+@@ -0,0 +1,567 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.8-3-dev-bds-vs-2.5.32.diff,v 1.1 2002/08/30 19:01:07 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno, prio;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	hookno = NF_IP_LOCAL_OUT;
++	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		// there is an iptables mangle table FORWARD chain with
++		// priority -150. This chain should see the physical out-dev.
++		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev), okfn, prio + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.8-bds-against-2.4.18.diff b/br-nf-bds/patches/bridge-nf-0.0.8-bds-against-2.4.18.diff
new file mode 100644
index 0000000..ad742c7
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.8-bds-against-2.4.18.diff
@@ -0,0 +1,983 @@
+bridge-nf-0.0.8-bds - 26 May
+
+difference between 0.0.7 and 0.0.8:
+
+let iptables mangle table FORWARD chain see the physical out-dev
+
+--- linux/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ bridge-nf-0.0.8-bds/include/linux/netfilter.h	Sun May 26 12:49:04 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++		nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ bridge-nf-0.0.8-bds/include/linux/netfilter_ipv4.h	Sun May 26 12:52:30 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux/include/linux/skbuff.h	Thu Nov 22 20:46:26 2001
++++ bridge-nf-0.0.8-bds/include/linux/skbuff.h	Sun May 26 12:49:04 2002
+@@ -135,6 +135,8 @@
+ 	struct sock	*sk;			/* Socket we are owned by 			*/
+ 	struct timeval	stamp;			/* Time we arrived				*/
+ 	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
++	struct net_device	*physindev;	/* Physical device we arrived on		*/
++	struct net_device	*physoutdev;	/* Physical device we will leave by		*/
+ 
+ 	/* Transport layer header */
+ 	union
+--- linux/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/bridge/br.c	Sun May 26 12:49:04 2002
+@@ -42,6 +42,11 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_BRIDGE_NF
++	if (br_netfilter_init())
++		return 1;
++#endif
++
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -65,6 +70,9 @@
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_BRIDGE_NF
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux/net/bridge/br_forward.c	Wed Aug 15 10:54:35 2001
++++ bridge-nf-0.0.8-bds/net/bridge/br_forward.c	Sun May 26 12:49:04 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -54,7 +54,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -65,7 +65,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux/net/bridge/br_input.c	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/bridge/br_input.c	Sun May 26 12:49:04 2002
+@@ -46,7 +46,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/bridge/br_private.h	Sun May 26 12:49:04 2002
+@@ -120,6 +120,7 @@
+ extern void br_inc_use_count(void);
+ 
+ /* br_device.c */
++extern int  br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+ extern void br_dev_setup(struct net_device *dev);
+ extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+ 
+@@ -144,8 +145,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int  br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int  br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +169,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int  br_handle_frame_finish(struct sk_buff *skb);
+ extern void br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -177,6 +181,10 @@
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ bridge-nf-0.0.8-bds/net/bridge/Makefile	Sun May 26 12:49:04 2002
+@@ -13,4 +13,6 @@
+ 			br_stp_if.o br_stp_timer.o
+ obj-m		:= $(O_TARGET)
+ 
++obj-$(CONFIG_BRIDGE_NF) += br_netfilter.o
++
+ include $(TOPDIR)/Rules.make
+--- linux/net/Config.in	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/Config.in	Sun May 26 12:49:04 2002
+@@ -61,6 +61,9 @@
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++   if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++      bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
++   fi
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+    bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+--- linux/net/core/netfilter.c	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/core/netfilter.c	Sun May 26 12:49:04 2002
+@@ -343,10 +343,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -414,6 +419,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -436,11 +443,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -450,7 +462,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -482,7 +495,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -531,7 +544,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux/net/core/skbuff.c	Fri Dec 21 18:42:05 2001
++++ bridge-nf-0.0.8-bds/net/core/skbuff.c	Sun May 26 12:49:04 2002
+@@ -231,6 +231,8 @@
+ 	skb->sk = NULL;
+ 	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	skb->dev = NULL;
++	skb->physindev = NULL;
++	skb->physoutdev = NULL;
+ 	skb->dst = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type = PACKET_HOST;	/* Default type */
+@@ -362,6 +364,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -417,6 +421,8 @@
+ 	new->list=NULL;
+ 	new->sk=NULL;
+ 	new->dev=old->dev;
++	new->physindev=old->physindev;
++	new->physoutdev=old->physoutdev;
+ 	new->priority=old->priority;
+ 	new->protocol=old->protocol;
+ 	new->dst=dst_clone(old->dst);
+--- linux/net/ipv4/ip_output.c	Wed Oct 17 23:16:39 2001
++++ bridge-nf-0.0.8-bds/net/ipv4/ip_output.c	Sun May 26 12:49:04 2002
+@@ -819,6 +819,8 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -882,6 +884,7 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++		memcpy(skb2->data - 16, skb->data - 16, 16);
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux/net/ipv4/netfilter/ip_tables.c	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/ipv4/netfilter/ip_tables.c	Sun May 26 12:49:04 2002
+@@ -121,12 +121,15 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +159,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +178,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +283,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +293,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +331,7 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev, &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ bridge-nf-0.0.8-bds/net/ipv4/netfilter/ipt_LOG.c	Sun May 26 12:49:04 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ bridge-nf-0.0.8-bds/net/bridge/br_netfilter.c	Sun May 26 12:59:00 2002
+@@ -0,0 +1,567 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.8-bds-against-2.4.18.diff,v 1.1 2002/06/01 19:24:01 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * for ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno, prio;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++	hookno = NF_IP_LOCAL_OUT;
++	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		// there is an iptables mangle table FORWARD chain with
++		// priority -150. This chain should see the physical out-dev.
++		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev), okfn, prio + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.9-bds-vs-2.4.20-pre5.diff b/br-nf-bds/patches/bridge-nf-0.0.9-bds-vs-2.4.20-pre5.diff
new file mode 100644
index 0000000..5268b57
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.9-bds-vs-2.4.20-pre5.diff
@@ -0,0 +1,1002 @@
+bridge-nf-0.0.9-bds vs 2.4.20-pre5 - 18 September
+
+--- linux-2.4.20-pre5-ebtables/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.20-pre5-full/include/linux/netfilter.h	Wed Sep 18 21:13:33 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.20-pre5-ebtables/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.20-pre5-full/include/linux/netfilter_ipv4.h	Wed Sep 18 21:13:33 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.20-pre5-ebtables/include/linux/skbuff.h	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-full/include/linux/skbuff.h	Wed Sep 18 21:13:33 2002
+@@ -135,6 +135,8 @@
+ 	struct sock	*sk;			/* Socket we are owned by 			*/
+ 	struct timeval	stamp;			/* Time we arrived				*/
+ 	struct net_device	*dev;		/* Device we arrived on/are leaving by		*/
++	struct net_device	*physindev;     /* Physical device we arrived on                */
++	struct net_device	*physoutdev;    /* Physical device we will leave by             */
+ 
+ 	/* Transport layer header */
+ 	union
+--- linux-2.4.20-pre5-ebtables/net/bridge/br.c	Wed Sep 18 21:01:01 2002
++++ linux-2.4.20-pre5-full/net/bridge/br.c	Wed Sep 18 21:13:33 2002
+@@ -44,6 +44,8 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++	if (br_netfilter_init())
++		return 1;
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -67,6 +69,7 @@
+ 
+ static void __exit br_deinit(void)
+ {
++	br_netfilter_fini();
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.4.20-pre5-ebtables/net/bridge/br_forward.c	Wed Sep 18 21:01:01 2002
++++ linux-2.4.20-pre5-full/net/bridge/br_forward.c	Wed Sep 18 21:13:33 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +53,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +64,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.20-pre5-ebtables/net/bridge/br_input.c	Wed Sep 18 21:01:01 2002
++++ linux-2.4.20-pre5-full/net/bridge/br_input.c	Wed Sep 18 21:13:33 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.4.20-pre5-ebtables/net/bridge/br_private.h	Wed Sep 18 21:01:01 2002
++++ linux-2.4.20-pre5-full/net/bridge/br_private.h	Wed Sep 18 21:13:33 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.4.20-pre5-ebtables/net/bridge/Makefile	Wed Sep 18 21:01:01 2002
++++ linux-2.4.20-pre5-full/net/bridge/Makefile	Wed Sep 18 21:13:33 2002
+@@ -12,7 +12,7 @@
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+-			br_stp_if.o br_stp_timer.o
++			br_stp_if.o br_stp_timer.o br_netfilter.o
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.20-pre5-ebtables/net/core/netfilter.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-full/net/core/netfilter.c	Wed Sep 18 21:13:33 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +442,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +461,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +494,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +543,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.20-pre5-ebtables/net/core/skbuff.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-full/net/core/skbuff.c	Wed Sep 18 21:13:33 2002
+@@ -231,6 +231,8 @@
+ 	skb->sk = NULL;
+ 	skb->stamp.tv_sec=0;	/* No idea about time */
+ 	skb->dev = NULL;
++	skb->physindev = NULL;
++	skb->physoutdev = NULL;
+ 	skb->dst = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type = PACKET_HOST;	/* Default type */
+@@ -362,6 +364,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -417,6 +421,8 @@
+ 	new->list=NULL;
+ 	new->sk=NULL;
+ 	new->dev=old->dev;
++	new->physindev=old->physindev;
++	new->physoutdev=old->physoutdev;
+ 	new->priority=old->priority;
+ 	new->protocol=old->protocol;
+ 	new->dst=dst_clone(old->dst);
+--- linux-2.4.20-pre5-ebtables/net/ipv4/ip_output.c	Wed Sep 18 20:50:57 2002
++++ linux-2.4.20-pre5-full/net/ipv4/ip_output.c	Wed Sep 18 21:13:33 2002
+@@ -830,6 +830,8 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -893,6 +895,9 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++
++		// for bridge-netfilter
++		memcpy(skb2->data - 16, skb->data - 16, 16);
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.4.20-pre5-ebtables/net/ipv4/netfilter/ip_tables.c	Wed Sep 18 20:50:57 2002
++++ linux-2.4.20-pre5-full/net/ipv4/netfilter/ip_tables.c	Wed Sep 18 21:13:33 2002
+@@ -122,12 +122,14 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+-	unsigned long ret;
++	unsigned long ret, ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -157,7 +159,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -170,7 +178,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -269,6 +283,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -278,6 +293,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -313,7 +331,8 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.20-pre5-ebtables/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-full/net/ipv4/netfilter/ipt_LOG.c	Wed Sep 18 21:13:33 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-full/net/bridge/br_netfilter.c	Wed Sep 18 21:13:33 2002
+@@ -0,0 +1,610 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.9-bds-vs-2.4.20-pre5.diff,v 1.1 2002/09/18 20:47:52 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++	skb->dst->output(skb);
++	return 0;
++}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++#define __br_handle_frame_finish  br_nf_pre_routing_finish_route
++static inline int br_nf_pre_routing_finish_route(struct sk_buff *skb)
++{
++	skb->nf_debug = 0;
++	br_handle_frame_finish(skb);
++	return 0;
++}
++#else
++#define __br_handle_frame_finish br_handle_frame_finish
++#endif
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			__br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	(*pskb)->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_FORWARD);
++#endif
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT
++***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_FORWARD);
++#endif
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno, prio;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_LOCAL_OUT);
++#endif
++	hookno = NF_IP_LOCAL_OUT;
++	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		// there is an iptables mangle table FORWARD chain with
++		// priority -150. This chain should see the physical out-dev.
++		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev), okfn, prio + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_POST_ROUTING);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/patches/bridge-nf-0.0.9-dev-bds-vs-2.5.35.diff b/br-nf-bds/patches/bridge-nf-0.0.9-dev-bds-vs-2.5.35.diff
new file mode 100644
index 0000000..7d295cb
--- /dev/null
+++ b/br-nf-bds/patches/bridge-nf-0.0.9-dev-bds-vs-2.5.35.diff
@@ -0,0 +1,1011 @@
+bridge-nf-0.0.9-dev-bds vs 2.5.35 - 18 September
+
+--- linux-2.5.35-ebtables/include/linux/netfilter.h	Mon Sep 16 04:18:29 2002
++++ linux-2.5.35/include/linux/netfilter.h	Wed Sep 18 22:51:08 2002
+@@ -117,17 +117,23 @@
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.5.35-ebtables/include/linux/netfilter_ipv4.h	Mon Sep 16 04:18:50 2002
++++ linux-2.5.35/include/linux/netfilter_ipv4.h	Wed Sep 18 22:51:08 2002
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.5.35-ebtables/include/linux/skbuff.h	Mon Sep 16 04:18:18 2002
++++ linux-2.5.35/include/linux/skbuff.h	Wed Sep 18 22:51:08 2002
+@@ -140,6 +140,8 @@
+  *	@sk: Socket we are owned by
+  *	@stamp: Time we arrived
+  *	@dev: Device we arrived on/are leaving by
++ *	@physindev: Phsical device we arrived on (see bridge-nf)
++ *	@physoutdev: Phsical device we will leave by (see bridge-nf)
+  *	@h: Transport layer header
+  *	@nh: Network layer header
+  *	@mac: Link layer header
+@@ -178,6 +180,8 @@
+ 	struct sock		*sk;
+ 	struct timeval		stamp;
+ 	struct net_device	*dev;
++	struct net_device	*physindev;
++	struct net_device	*physoutdev;
+ 
+ 	union {
+ 		struct tcphdr	*th;
+--- linux-2.5.35-ebtables/net/bridge/br.c	Wed Sep 18 21:39:32 2002
++++ linux-2.5.35/net/bridge/br.c	Wed Sep 18 22:51:08 2002
+@@ -44,6 +44,8 @@
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++	if (br_netfilter_init())
++		return 1;
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -67,6 +69,7 @@
+ 
+ static void __exit br_deinit(void)
+ {
++	br_netfilter_fini();
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 	net_call_rx_atomic(__br_clear_frame_hook);
+--- linux-2.5.35-ebtables/net/bridge/br_forward.c	Wed Sep 18 21:39:32 2002
++++ linux-2.5.35/net/bridge/br_forward.c	Wed Sep 18 22:51:08 2002
+@@ -30,7 +30,7 @@
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+@@ -38,10 +38,10 @@
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -53,7 +53,7 @@
+ 	skb->nf_debug = 0;
+ #endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -64,7 +64,7 @@
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.5.35-ebtables/net/bridge/br_input.c	Wed Sep 18 21:39:32 2002
++++ linux-2.5.35/net/bridge/br_input.c	Wed Sep 18 22:51:08 2002
+@@ -49,7 +49,7 @@
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+--- linux-2.5.35-ebtables/net/bridge/br_private.h	Wed Sep 18 21:39:32 2002
++++ linux-2.5.35/net/bridge/br_private.h	Wed Sep 18 22:51:08 2002
+@@ -144,8 +144,10 @@
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,6 +168,7 @@
+ 			   int *ifindices);
+ 
+ /* br_input.c */
++extern int br_handle_frame_finish(struct sk_buff *skb);
+ extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+@@ -176,6 +179,10 @@
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.5.35-ebtables/net/bridge/Makefile	Wed Sep 18 21:39:32 2002
++++ linux-2.5.35/net/bridge/Makefile	Wed Sep 18 22:51:08 2002
+@@ -8,7 +8,7 @@
+ 
+ bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+-			br_stp_if.o br_stp_timer.o
++			br_stp_if.o br_stp_timer.o br_netfilter.o
+ obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.5.35-ebtables/net/core/netfilter.c	Mon Sep 16 04:18:28 2002
++++ linux-2.5.35/net/core/netfilter.c	Wed Sep 18 22:51:08 2002
+@@ -342,10 +342,15 @@
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,8 @@
+ {
+ 	int status;
+ 	struct nf_info *info;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +442,16 @@
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++	if ((physindev = skb->physindev)) dev_hold(physindev);
++	if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +461,8 @@
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +494,7 @@
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -530,7 +543,7 @@
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.5.35-ebtables/net/core/skbuff.c	Mon Sep 16 04:18:22 2002
++++ linux-2.5.35/net/core/skbuff.c	Wed Sep 18 22:51:08 2002
+@@ -234,6 +234,8 @@
+ 	skb->sk		  = NULL;
+ 	skb->stamp.tv_sec = 0;	/* No idea about time */
+ 	skb->dev	  = NULL;
++	skb->physindev	  = NULL;
++	skb->physoutdev	  = NULL;
+ 	skb->dst	  = NULL;
+ 	memset(skb->cb, 0, sizeof(skb->cb));
+ 	skb->pkt_type	  = PACKET_HOST;	/* Default type */
+@@ -363,6 +365,8 @@
+ 	n->sk = NULL;
+ 	C(stamp);
+ 	C(dev);
++	C(physindev);
++	C(physoutdev);
+ 	C(h);
+ 	C(nh);
+ 	C(mac);
+@@ -418,6 +422,8 @@
+ 	new->list	= NULL;
+ 	new->sk		= NULL;
+ 	new->dev	= old->dev;
++	new->physindev	= old->physindev;
++	new->physoutdev	= old->physoutdev;
+ 	new->priority	= old->priority;
+ 	new->protocol	= old->protocol;
+ 	new->dst	= dst_clone(old->dst);
+--- linux-2.5.35-ebtables/net/ipv4/ip_output.c	Mon Sep 16 04:19:00 2002
++++ linux-2.5.35/net/ipv4/ip_output.c	Wed Sep 18 22:51:08 2002
+@@ -845,6 +845,8 @@
+ 			skb_set_owner_w(skb2, skb->sk);
+ 		skb2->dst = dst_clone(skb->dst);
+ 		skb2->dev = skb->dev;
++		skb2->physindev = skb->physindev;
++		skb2->physoutdev = skb->physoutdev;
+ 
+ 		/*
+ 		 *	Copy the packet header into the new buffer.
+@@ -908,6 +910,9 @@
+ 		iph->tot_len = htons(len + hlen);
+ 
+ 		ip_send_check(iph);
++
++		// for bridge-netfilter
++		memcpy(skb2->data - 16, skb->data - 16, 16);
+ 
+ 		err = output(skb2);
+ 		if (err)
+--- linux-2.5.35-ebtables/net/ipv4/netfilter/ip_tables.c	Mon Sep 16 04:18:23 2002
++++ linux-2.5.35/net/ipv4/netfilter/ip_tables.c	Wed Sep 18 22:51:08 2002
+@@ -121,12 +121,14 @@
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++		const char *physindev,
+ 		const char *outdev,
++		const char *physoutdev,
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+-	unsigned long ret;
++	unsigned long ret, ret2;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +158,13 @@
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +177,13 @@
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +282,7 @@
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++	const char *physindev, *physoutdev;
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +292,9 @@
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++	physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
++	physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -311,7 +329,8 @@
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.5.35-ebtables/net/ipv4/netfilter/ipt_LOG.c	Mon Sep 16 04:18:25 2002
++++ linux-2.5.35/net/ipv4/netfilter/ipt_LOG.c	Wed Sep 18 22:51:08 2002
+@@ -285,10 +285,13 @@
+ 	level_string[1] = '0' + (loginfo->level % 8);
+ 	spin_lock_bh(&log_lock);
+ 	printk(level_string);
+-	printk("%sIN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
++	if ((*pskb)->physindev && in != (*pskb)->physindev)
++		printk("PHYSIN=%s ", (*pskb)->physindev->name);
++	printk("OUT=%s ", out ? out->name : "");
++	if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
++		printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.35/net/bridge/br_netfilter.c	Wed Sep 18 22:51:08 2002
+@@ -0,0 +1,610 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	$Id: bridge-nf-0.0.9-dev-bds-vs-2.5.35.diff,v 1.2 2002/09/18 21:45:53 bdschuym Exp $
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++/* As the original source/destination addresses are variables private to this
++ * file, we store them in unused space at the end of the control buffer.
++ * On 64-bit platforms the TCP control buffer size still leaves us 8 bytes
++ * of space at the end, so that fits.  Usage of the original source address
++ * and the original destination address never overlaps (daddr is needed
++ * around PRE_ROUTING, and saddr around POST_ROUTING), so that's okay as
++ * well.
++ */
++#define skb_origaddr(skb)		(*((u32 *)((skb)->cb + sizeof((skb)->cb) - 4)))
++
++#define store_orig_dstaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define store_orig_srcaddr(skb)		(skb_origaddr(skb) = (skb)->nh.iph->saddr)
++#define dnat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define snat_took_place(skb)		(skb_origaddr(skb) != (skb)->nh.iph->saddr)
++#else
++#define store_orig_dstaddr(skb)
++#define store_orig_srcaddr(skb)
++#define dnat_took_place(skb)		(0)
++#define snat_took_place(skb)		(0)
++#endif
++
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++
++/* As opposed to the DNAT case, for the SNAT case it's not quite
++ * clear what we should do with ethernet addresses in NAT'ed
++ * packets.  Use this heuristic for now.
++ */
++static inline void __maybe_fixup_src_address(struct sk_buff *skb)
++{
++	if (snat_took_place(skb) &&
++	    inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) {
++		memcpy(skb->mac.ethernet->h_source,
++			bridge_parent(skb->dev)->dev_addr,
++			ETH_ALEN);
++	}
++}
++
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	hard_header_len:	ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++	skb->dev = bridge_parent(skb->dev);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++	skb->dst->output(skb);
++	return 0;
++}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++#define __br_handle_frame_finish  br_nf_pre_routing_finish_route
++static inline int br_nf_pre_routing_finish_route(struct sk_buff *skb)
++{
++	skb->nf_debug = 0;
++	br_handle_frame_finish(skb);
++	return 0;
++}
++#else
++#define __br_handle_frame_finish br_handle_frame_finish
++#endif
++
++/* This requires some explaining.  If DNAT has taken place,
++ * we will need to fix up the destination ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on.  We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The way to distinguish between the two is by calling ip_route_input()
++ * and looking at skb->dst->dev, which it changed to the destination device
++ * if ip_route_input() succeeds.
++ *
++ * Let us first consider ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet came in on,
++ * we can consider this bridging. We then call skb->dst->output() which will
++ * make the packet enter br_nf_local_out() not much later. In that function
++ * it is assured that the iptables FORWARD chain is traversed for the packet.
++ *
++ * Else, the packet is considered to be routed and we just change the
++ * destination MAC address so that the packet will later be passed up to the ip
++ * stack to be routed.
++ *
++ * Let us now consider ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input() will
++ * fail, while ip_route_output() will return success. The source address for
++ * ip_route_output() is set to zero, so ip_route_output()
++ * thinks we're handling a locally generated packet and won't care if
++ * ip forwarding is allowed. We send a warning message to the users's log
++ * telling her to put ip forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available. Then we just
++ * drop the packet.
++ *
++ * The other special thing happening here is putting skb->physoutdev on
++ * &__fake_net_device (resp. NULL) for bridged (resp. routed) packets. This is
++ * needed so that br_nf_local_out() can know that it has to give the packets to
++ * the BR_NF_FORWARD (resp. BR_NF_LOCAL_OUT) bridge hook. See that function.
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ */
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				// bridged dnated traffic isn't dependent on
++				// disabled ip_forwarding
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				// tell br_nf_local_out this is a bridged frame
++				skb->physoutdev = &__fake_net_device;
++				skb->dev = skb->physindev;
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++						br_nf_pre_routing_finish_bridge, 1);
++				return 0;
++			}
++			// tell br_nf_local_out this is a routed frame
++			skb->physoutdev = NULL;
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr, ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++	skb->dev = skb->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++			__br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++	skb->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->pkt_type == PACKET_OTHERHOST)
++		skb->pkt_type = PACKET_HOST;
++	store_orig_dstaddr(skb);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	(*pskb)->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because another bit of the bridge-nf patch overloads the
++ * '-i' and '-o' iptables interface checks to take
++ * skb->phys{in,out}dev into account as well (so both the real
++ * device and the bridge device will match).
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	// don't mess with non-ip frames, also don't mess with the ip-packets
++	// when br_nf_local_out_finish explicitly says so.
++	if (skb->protocol != __constant_htons(ETH_P_IP) || skb->physindev == NULL)
++		return NF_ACCEPT;
++
++	skb->physoutdev = skb->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_FORWARD);
++#endif
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT
++***********************************************/
++static int br_nf_local_out_finish_forward(struct sk_buff *skb)
++{
++	struct net_device *dev;
++
++	dev = skb->physindev;
++	// tell br_nf_forward to stay away
++	skb->physindev = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_FORWARD);
++#endif
++	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, dev, skb->dev,
++		br_forward_finish);
++
++	return 0;
++}
++
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, INT_MIN + 1);
++
++	return 0;
++}
++
++
++/* This hook sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device).  For the sake of interface transparency (i.e. properly
++ * overloading the '-o' option), we steal packets destined to
++ * a bridge device away from the IPv4 FORWARD and OUTPUT hooks,
++ * and reinject them later, when we have determined the real
++ * output device.  This reinjecting happens here.
++ *
++ * If skb->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated.
++ * We call the IPv4 LOCAL_OUT hook.
++ *
++ * If skb->physindev isn't NULL, there are two cases:
++ * 1. The packet was IP routed.
++ * 2. The packet was cross-bridge DNAT'ed (see the comment near
++ *    PF_BRIDGE/PRE_ROUTING).
++ * In both cases, we call the IPv4 FORWARD hook.  In case 1,
++ * if the packet originally came from a bridge device, and in
++ * case 2, skb->physindev will have a bridge device as parent,
++ * so we use that parent device as indev.  Otherwise, we just
++ * use physindev.
++ *
++ * If skb->physoutdev == NULL the bridge code never touched the
++ * packet or the packet was routed in br_nf_pre_routing_finish().
++ * We give the packet to the bridge NF_BR_LOCAL_OUT hook.
++ * If not, the packet is actually a bridged one so we give it to
++ * the NF_BR_FORWARD hook.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*_okfn)(struct sk_buff *))
++{
++	int hookno, prio;
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	// bridged, take forward
++	// (see big note in front of br_nf_pre_routing_finish)
++	if (skb->physoutdev == &__fake_net_device) {
++		okfn = br_nf_local_out_finish_forward;
++	} else if (skb->physoutdev == NULL) {
++		// non-bridged: routed or locally generated traffic, take local_out
++		// (see big note in front of br_nf_pre_routing_finish)
++		okfn = br_nf_local_out_finish;
++	} else {
++		printk("ARGH: bridge_or_routed hack doesn't work\n");
++		okfn = br_nf_local_out_finish;
++	}
++
++	skb->physoutdev = skb->dev;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_LOCAL_OUT);
++#endif
++	hookno = NF_IP_LOCAL_OUT;
++	prio = NF_IP_PRI_BRIDGE_SABOTAGE;
++	if ((realindev = skb->physindev) != NULL) {
++		hookno = NF_IP_FORWARD;
++		// there is an iptables mangle table FORWARD chain with
++		// priority -150. This chain should see the physical out-dev.
++		prio = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD;
++		if (has_bridge_parent(realindev))
++			realindev = bridge_parent(realindev);
++	}
++
++	NF_HOOK_THRESH(PF_INET, hookno, skb, realindev,
++			bridge_parent(skb->dev), okfn, prio + 1);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static int br_nf_post_routing_finish(struct sk_buff *skb)
++{
++	__maybe_fixup_src_address(skb);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_POST_ROUTING);
++#endif
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL,
++			bridge_parent(skb->dev), br_dev_queue_push_xmit, 1);
++
++	return 0;
++}
++
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	/* Be very paranoid.  */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "Argh!! Fuck me harder with a chainsaw. ");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	store_orig_srcaddr(skb);
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_nf_post_routing_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.  */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_nf_post_routing_finish) {
++		struct sk_buff *skb = *pskb;
++
++		if (hook == NF_IP_FORWARD && skb->physindev == NULL)
++			skb->physindev = (struct net_device *)in;
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++static struct nf_hook_ops br_nf_ops[] = {
++	{ { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, 0 },
++	{ { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, 0 },
++	{ { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, 0 },
++	// we need INT_MIN, so innocent NF_BR_LOCAL_OUT functions don't
++	// get bridged traffic as input
++	{ { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, INT_MIN },
++	{ { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, 0 },
++
++	{ { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
++
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE },
++	{ { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST },
++};
++
++#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
++
++
++int br_netfilter_init(void)
++{
++	int i;
++
++#ifndef WE_REALLY_INSIST_ON_NOT_HAVING_NAT_SUPPORT
++	if (sizeof(struct tcp_skb_cb) + 4 >= sizeof(((struct sk_buff *)NULL)->cb)) {
++		extern int __too_little_space_in_control_buffer(void);
++		__too_little_space_in_control_buffer();
++	}
++#endif
++
++	for (i=0;i<NUMHOOKS;i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i=NUMHOOKS-1;i>=0;i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
diff --git a/br-nf-bds/scripts/Make_brnf_diff b/br-nf-bds/scripts/Make_brnf_diff
new file mode 100755
index 0000000..a220919
--- /dev/null
+++ b/br-nf-bds/scripts/Make_brnf_diff
@@ -0,0 +1,29 @@
+#!/bin/bash
+# for making the bridge-nf-bds patches
+# 18 September 2002: removed net/Config.in
+# 9 October 2002: added include/linux/netfilter_bridge.h
+# 19 October 2002: removed ip_output.c
+
+export FROM=linux-2.4.20
+export TO=linux-2.4.20-patch
+export FILE=bridge-nf-0.0.10-against-2.4.20.diff
+
+echo "bridge-nf-0.0.10-against-2.4.20 - 07 December 2002" >$FILE
+echo >>$FILE
+
+diff -urN $FROM/include/linux/netfilter.h $TO/include/linux/netfilter.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_ipv4.h $TO/include/linux/netfilter_ipv4.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge.h $TO/include/linux/netfilter_bridge.h >> $FILE
+diff -urN $FROM/include/linux/skbuff.h $TO/include/linux/skbuff.h >> $FILE
+
+diff -urN $FROM/net/bridge/br.c $TO/net/bridge/br.c >> $FILE
+diff -urN $FROM/net/bridge/br_forward.c $TO/net/bridge/br_forward.c >> $FILE
+diff -urN $FROM/net/bridge/br_input.c $TO/net/bridge/br_input.c >> $FILE
+diff -urN $FROM/net/bridge/br_private.h $TO/net/bridge/br_private.h >> $FILE
+diff -urN $FROM/net/bridge/Makefile $TO/net/bridge/Makefile >> $FILE
+diff -urN $FROM/net/core/netfilter.c $TO/net/core/netfilter.c >> $FILE
+diff -urN $FROM/net/core/skbuff.c $TO/net/core/skbuff.c >> $FILE
+diff -urN $FROM/net/ipv4/netfilter/ip_tables.c $TO/net/ipv4/netfilter/ip_tables.c >> $FILE
+diff -urN $FROM/net/ipv4/ip_output.c $TO/net/ipv4/ip_output.c >> $FILE
+diff -urN $FROM/net/ipv4/netfilter/ipt_LOG.c $TO/net/ipv4/netfilter/ipt_LOG.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/br_netfilter.c >> $FILE
diff --git a/br-nf-bds/scripts/Make_brnf_diff_incr b/br-nf-bds/scripts/Make_brnf_diff_incr
new file mode 100755
index 0000000..56699f4
--- /dev/null
+++ b/br-nf-bds/scripts/Make_brnf_diff_incr
@@ -0,0 +1,27 @@
+#!/bin/bash
+# for making the bridge-nf-bds patches
+# 18 September 2002: removed net/Config.in
+# 9 October 2002: added include/linux/netfilter_bridge.h
+
+export FROM=linux-2.5.42
+export TO=linux-2.5.42-brnf
+export FILE=bridge-nf-0.0.10-dev-pre2.001-against-2.5.42.diff
+
+echo "bridge-nf-0.0.10-dev-pre2.001-against-2.5.42 - 19 October" >$FILE
+echo >>$FILE
+
+diff -urN $FROM/include/linux/netfilter.h $TO/include/linux/netfilter.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_ipv4.h $TO/include/linux/netfilter_ipv4.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge.h $TO/include/linux/netfilter_bridge.h >> $FILE
+diff -urN $FROM/include/linux/skbuff.h $TO/include/linux/skbuff.h >> $FILE
+
+diff -urN $FROM/net/bridge/br.c $TO/net/bridge/br.c >> $FILE
+diff -urN $FROM/net/bridge/br_forward.c $TO/net/bridge/br_forward.c >> $FILE
+diff -urN $FROM/net/bridge/br_input.c $TO/net/bridge/br_input.c >> $FILE
+diff -urN $FROM/net/bridge/br_private.h $TO/net/bridge/br_private.h >> $FILE
+diff -urN $FROM/net/bridge/Makefile $TO/net/bridge/Makefile >> $FILE
+diff -urN $FROM/net/core/netfilter.c $TO/net/core/netfilter.c >> $FILE
+diff -urN $FROM/net/core/skbuff.c $TO/net/core/skbuff.c >> $FILE
+diff -urN $FROM/net/ipv4/netfilter/ip_tables.c $TO/net/ipv4/netfilter/ip_tables.c >> $FILE
+diff -urN $FROM/net/ipv4/netfilter/ipt_LOG.c $TO/net/ipv4/netfilter/ipt_LOG.c >> $FILE
+diff -urN $FROM/net/bridge/br_netfilter.c $TO/net/bridge/br_netfilter.c >> $FILE
diff --git a/docs/arptables-faq.html b/docs/arptables-faq.html
new file mode 100644
index 0000000..57ed467
--- /dev/null
+++ b/docs/arptables-faq.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<TITLE>Arptables Frequently Asked Questions</TITLE>
+	<LINK rel="SHORTCUT ICON" href="">
+	<LINK rel="STYLESHEET" type="text/css" href="brnf.css">
+	<META name="description" content="Arptables Frequently Asked Questions">
+	<META name="author" content="Bart De Schuymer">
+	<META name="keywords" content="Linux, netfilter, firewall, bridge, arptables">
+	<META name="keywords" content="FAQ, kernel, arptables, chains, rules, tables">
+</HEAD>
+<BODY>
+	<DIV class="banner" align="center">
+		<H1>Arptables Frequently (and less frequently) Asked Questions</H1>
+	</DIV>
+	<A name="top"></A>
+	<P>Last modified: December 30, 2003</P>
+	<DL>
+		<DT>
+Why does arptables have 2 chains on a 2.4 kernel and 3 chains
+on a 2.6 kernel?
+		</DT>
+		<DD>
+The 2.4 kernel doesn't have the arptables FORWARD chain as 2.4
+kernels can't filter bridged ARP traffic.
+		</DD>
+		<DT>
+When is the bridged ARP traffic seen by arptables?
+		</DT>
+		<DD>
+The arptables FORWARD chain sees all ARP packets that are being
+bridged, it sees no other traffic.
+		</DD>
+		<DT>
+What about ARP packets that arrive through a bridge port and
+are delivered to the bridge's local ARP stack?
+		</DT>
+		<DD>
+They are seen in the arptables INPUT chain and have as input
+device the logical bridge device, unless you broute them
+using ebtables. Brouted packets will have the physical bridge
+port as input device.
+		</DD>
+		<DT>
+What about locally generated ARP packets that leave the bridge
+through a logical bridge device?
+		</DT>
+		<DD>
+They are seen in the arptables OUTPUT chain and have as output
+device the logical bridge device.
+		</DD>
+	</DL>
+	<A class=navbar href="#top">[Back to the top]</A>
+	<HR>
+</BODY>
+</HTML>
diff --git a/docs/br_fw_ia/PacketFlow.jpg b/docs/br_fw_ia/PacketFlow.jpg
new file mode 100644
index 0000000..83d1678
--- /dev/null
+++ b/docs/br_fw_ia/PacketFlow.jpg
Binary files differ
diff --git a/docs/br_fw_ia/PacketFlow.png b/docs/br_fw_ia/PacketFlow.png
new file mode 100644
index 0000000..97dd3e9
--- /dev/null
+++ b/docs/br_fw_ia/PacketFlow.png
Binary files differ
diff --git a/docs/br_fw_ia/PacketFlow.vsd b/docs/br_fw_ia/PacketFlow.vsd
new file mode 100644
index 0000000..1feaf2b
--- /dev/null
+++ b/docs/br_fw_ia/PacketFlow.vsd
Binary files differ
diff --git a/docs/br_fw_ia/br_fw_ia.css b/docs/br_fw_ia/br_fw_ia.css
new file mode 100644
index 0000000..11e7bdb
--- /dev/null
+++ b/docs/br_fw_ia/br_fw_ia.css
@@ -0,0 +1,34 @@
+H1 { FONT: bold 25pt Times, serif; TEXT-ALIGN: center; TEXT-DECORATION: none }
+P  {  FONT: 18pt Times, serif }
+LI {  FONT: 16pt Times, serif }
+PRE { FONT: 18pt Courier, monospace }
+
+.statement { TEXT-DECORATION: underline }
+.section { FONT: bold 22pt Times }
+.case { FONT-STYLE: italic }
+.note {  
+ font-family: Arial;
+ font-weight: normal;
+ font-size: 14pt;
+ padding-left: 0.4em;
+ border: solid;
+ border-width: thin;
+ border-left: solid;
+ border-right: none;
+ border-top: none;
+ border-bottom: none;
+ border-left-width: thin;
+ border-color: red;
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+
+DIV {
+ border: solid;
+ border-width: thin;
+ background-color: #ffff99;
+}
+
+BODY {
+ background-color: white;
+}
diff --git a/docs/br_fw_ia/br_fw_ia.html b/docs/br_fw_ia/br_fw_ia.html
new file mode 100644
index 0000000..bcd1e44
--- /dev/null
+++ b/docs/br_fw_ia/br_fw_ia.html
@@ -0,0 +1,638 @@
+<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01//EN'
+'http://www.w3.org/TR/html4/strict.dtd'>
+<HTML>
+  <HEAD>
+    <TITLE>
+      ebtables/iptables interaction on a Linux-based bridge
+    </TITLE>
+    <META HTTP-EQUIV="Content-Type" CONTENT=
+    "text/html; charset=iso-8859-1">
+    <LINK REL="STYLESHEET" TYPE="text/css" HREF="br_fw_ia.css">
+  </HEAD>
+  <BODY>
+    <DIV CLASS="bar">
+      <DIV CLASS="shadow">
+        <H1 ALIGN="center">
+          ebtables/iptables interaction on a Linux-based bridge
+        </H1>
+      </DIV>
+    </DIV>
+    <H3>
+      <A NAME="top">Table of Contents</A>
+    </H3>
+    <OL>
+      <LI>
+        <A HREF="#section1">Introduction</A>
+      </LI>
+      <LI>
+        <A HREF="#section2">How frames traverse the
+        <EM>ebtables</EM> chains</A>
+      </LI>
+      <LI>
+        <A HREF="#section3">A machine used as a bridge and a
+        router (not a brouter)</A>
+      </LI>
+      <LI>
+        <A HREF="#section4">DNAT'ing bridged packets</A>
+      </LI>
+      <LI>
+        <A HREF="#section5">Chain traversal for bridged IP packets</A>
+      </LI>
+      <LI>
+        <A HREF="#section6">Using a bridge port in <EM>iptables</EM> rules</A>
+      </LI>
+      <LI>
+        <A HREF="#section7">Two possible ways for frames/packets
+        to pass through the <EM>iptables</EM> PREROUTING, FORWARD and
+        POSTROUTING chains</A>
+      </LI>
+      <LI>
+        <A HREF="#section8">IP DNAT in the <EM>iptables</EM> PREROUTING
+        chain on frames/packets entering on a bridge port</A>
+      </LI>
+      <LI>
+        <A HREF="#section9">Using the MAC module extension for
+        <EM>iptables</EM></A>
+      </LI>
+      <LI>
+        <A HREF="#section10">Using the <EM>iptables</EM> physdev match module for kernel 2.6</A>
+      </LI>
+      <LI>
+        <A HREF="#section11">Detailed IP packet flow</A>
+      </LI>
+    </OL>
+    <A NAME="section1"></A>
+    <P CLASS="section">
+      1. Introduction
+    </P>
+    <P>
+      This document describes how <EM>iptables</EM> and
+      <EM>ebtables</EM> filtering tables interact on a Linux-based bridge.<BR>
+      Getting a bridging firewall on a 2.4.x kernel consists of patching the kernel source
+      code. The 2.6 kernel contains the <EM>ebtables</EM> and <EM>br-nf</EM> code, so it doesn't have to be patched.
+      Because the demand was high, patches for the 2.4 kernel are still available at the <EM>ebtables</EM> homepage.
+      The <EM>br-nf</EM> code makes bridged IP frames/packets go through the <EM>iptables</EM> chains.
+      <EM>Ebtables</EM> filters on the Ethernet layer, while <EM>iptables</EM>
+      only filters IP packets.<BR>
+      The explanations below will use the TCP/IP Network Model.
+      It should be noted that the <EM>br-nf</EM> code sometimes violates the
+      TCP/IP Network
+      Model. As will be seen later, it is possible, f.e., to do IP DNAT inside the Link Layer.<BR>
+      We want to note that we are perfectly well aware that the word frame is used for the Link Layer,
+      while the word packet is used for the Network Layer. However, when we are talking about IP packets
+      inside the Link Layer, we will refer to these as frames/packets or packets/frames.
+    </P>
+    <A NAME="section2"></A>
+    <P CLASS="section">
+      2. How frames traverse the <EM>ebtables</EM> chains
+    </P>
+    <DIV CLASS="note">
+      This section only considers <EM>ebtables</EM>, not
+      <EM>iptables</EM>.
+    </DIV>
+    <P>
+      First thing to keep in mind is that we are talking about
+      the Ethernet layer here, so the OSI layer 2 (Data link
+      layer), or layer 1 (Link layer, Network Access layer) by the TCP/IP Network
+      Model.
+    </P>
+    <P>
+      A packet destined for the local computer according to the
+      bridge (which works on the Ethernet layer) isn't
+      necessarily destined for the local computer according to
+      the IP layer. That's how routing works (MAC destination is
+      the router, IP destination is the actual box you want to
+      communicate with).
+    </P>
+    <P>
+      <IMG SRC="bridge2a.png">
+    </P>
+    <P>
+      <I><B>Figure 2a.</B> General frame traversal scheme</I><BR>
+    </P>
+    <P>
+    </P>
+    <P>
+      There are six hooks defined in the Linux bridging code, of which the
+      BROUTING hook was added for <EM>ebtables</EM>.
+    </P>
+    <BR>
+     <BR>
+     <IMG SRC="bridge2b.png">
+    <P>
+      <I><B>Figure 2b.</B> Ethernet bridging hooks</I><BR>
+    </P>
+    </P>
+    <P>
+    <P>
+      The hooks are specific places in the network
+      code on which software can attach itself to process the
+      packets/frames passing that place. For example, the kernel module responsible for the <EM>ebtables</EM> FORWARD chain is attached onto the bridge FORWARD hook.
+      This is done when the module is loaded into the kernel or at bootup.
+    </P>
+    <P>
+     Note that the <EM>ebtables</EM> BROUTING and PREROUTING chains are traversed before the bridging decision, therefore these chains will even see frames that will be
+     ignored by the bridge. You should take that into account when using this chain. Also note that the chains won't see frames entering on a non-forwarding bridge port.<br>
+     The bridge's decision for a frame (as seen on Figure 2b) can be one of these:
+      <ul>
+      <li>bridge it, if the destination MAC address is on
+      another side of the bridge;</li>
+      <li>flood it over all the forwarding bridge ports, if the
+      position of the box with the destination MAC is unknown
+      to the bridge;</li>
+      <li>pass it to the higher protocol code (the IP code),
+      if the destination MAC address is that of the bridge or of
+      one of its ports;</li>
+      <li>ignore it, if the destination MAC address is located
+      on the same side of the bridge.</li>
+      </ul>
+    </P>
+     <IMG SRC="bridge2c.png">
+    <P>
+      <I><B>Figure 2c.</B> Bridging tables (ebtables) traversal
+      process</I><BR>
+    </P>
+    <P>
+      <EM>Ebtables</EM> has three tables:
+      <B>filter</B>, <B>nat</B> and <B>broute</B>, as shown in Figure 2c.
+    </P>
+    <UL>
+      <LI>
+        The <FONT COLOR="#00ffff"><B>broute</B></FONT> table has
+        the BROUTING chain.
+      </LI>
+      <LI>
+        The <FONT COLOR="#00ffff"><B>filter</B></FONT> table has
+        the FORWARD, INPUT and OUTPUT chains.
+      </LI>
+      <LI>
+        The <FONT COLOR="#00ffff"><B>nat</B></FONT> table has the
+        PREROUTING, OUTPUT and POSTROUTING chains.
+      </LI>
+    </UL>
+    <BR>
+    <DIV CLASS="note">
+      The filter OUTPUT and nat OUTPUT chains are separated and have
+      a different usage.
+    </DIV>
+    <P>
+      Figures 2b and 2c give a clear view where the
+      <EM>ebtables</EM> chains are attached onto the bridge hooks.
+    </P>
+    <P>
+      When an NIC enslaved to a bridge receives a frame, the frame
+      will first go through the BROUTING chain. In this special
+      chain you can choose whether to route or bridge frames,
+      enabling you to make a brouter. The definitions found on
+      the Internet for what a brouter actually is differ a bit.
+      The next definition describes the brouting ability using the
+      BROUTING chain quite well:
+    </P>
+    <DIV CLASS="note">
+      A brouter is a device that
+      bridges some frames/packets (i.e. forwards based on Link layer
+      information) and routes other frames/packets (i.e. forwards based
+      on Network layer information). The bridge/route decision is
+      based on configuration information.
+    </DIV>
+    <P>
+      A brouter can be used, for example,
+      to act as a normal router for IP traffic between 2
+      networks, while bridging specific traffic (NetBEUI, ARP,
+      whatever) between those networks. The IP routing
+      table does not use the bridge logical device, instead the box has
+      IP addresses assigned to the physical network devices that
+      also happen to be bridge ports (bridge enslaved NICs).<BR>
+      The default decision in the BROUTING chain is bridging.
+    </P>
+    <P>
+      Next the frame passes through the PREROUTING chain.
+      In this chain you can alter the destination MAC address
+      of frames (DNAT).
+      If the frame passes this chain, the bridging code will decide where the
+      frame should be sent. The bridge does this by looking at
+      the destination MAC address, it doesn't care about the
+      Network Layer addresses (e.g. IP address).
+    </P>
+    <P>
+      If the bridge decides the frame is destined for the local
+      computer, the frame will go through the INPUT chain.
+      In this chain you can filter frames destined for the bridge box.
+      After traversal of the INPUT chain, the frame will be passed up
+      to the Network Layer code (e.g. to the IP code).
+      So, a routed IP packet will go through
+      the <EM>ebtables</EM> INPUT chain, not through the
+      <EM>ebtables</EM> FORWARD chain. This is logical.
+    </P>
+     <BR>
+     <BR>
+     <IMG SRC="bridge2d.png">
+    <P>
+      <I><B>Figure 2d.</B> Incoming frame's chain traversal</I><BR>
+    </P>
+    <P>
+      Otherwise the frame should possibly be sent onto another side
+      of the bridge. If it should, the frame will go through the
+      FORWARD chain and the POSTROUTING chain. The bridged frames can be
+      filtered in the FORWARD chain. In the POSTROUTING chain you can alter the MAC
+      source address (SNAT).
+    </P>
+     <BR>
+     <BR>
+     <IMG SRC="bridge2e.png">
+    <P>
+      <I><B>Figure 2e.</B> Forwarded frame's chain traversal</I><BR>
+    </P>
+    <P>
+      Locally originated frames will, after the bridging decision, traverse
+      the nat OUTPUT, the filter OUTPUT and the nat POSTROUTING chains.
+      The nat OUTPUT chain allows to alter the destination
+      MAC address and the filter OUTPUT chain allows to
+      filter frames originating from the bridge box. Note that
+      the nat OUTPUT chain is traversed after the bridging
+      decision, so this is actually too late. We should change this. The nat
+      POSTROUTING chain is the same one as described above.
+    </P>
+     <BR>
+     <BR>
+     <IMG SRC="bridge2f.png">
+    <P>
+      <I><B>Figure 2f.</B> Outgoing frames' chain traversal</I><BR>
+    </P>
+    <DIV CLASS="note">
+      It's also possible for routed frames to go
+      through these three chains when the destination
+      device is a logical bridge device.
+    </DIV>
+    <BR>
+     <BR>
+     <A NAME="section3"></A>
+    <P CLASS="section">
+      3. A machine used as a bridge and a router (not a brouter)
+    </P>
+    <P>
+      Here is the IP code hooks scheme:
+    </P>
+    <IMG SRC="bridge3a.png"> 
+    <P>
+      <I><B>Figure 3a.</B> IP code hooks</I><BR>
+    </P>
+    <P>
+      Here is the iptables packet traversal scheme.
+    </P>
+    <BR>
+     <BR>
+     <IMG SRC="bridge3b.png">
+    <P>
+      <I><B>Figure 3b.</B> Routing tables (iptables) traversal
+      process</I><BR>
+    </P>
+    <P>
+      Note that the iptables nat OUTPUT chain is situated after the
+      routing decision. As commented in the previous section (when discussing ebtables nat),
+      this is too late for DNAT. This is solved by rerouting the
+      IP packet if it has been DNAT'ed, before continuing. For clarity:
+      this is standard behaviour of the Linux kernel, not something
+      caused by our code. 
+    </P>
+    <P>
+      Figures 3a and 3b give a clear view where the
+      <EM>iptables</EM> chains are attached onto the IP hooks. When the
+      bridge code and netfilter is enabled in the kernel, the iptables chains are
+      also attached onto the hooks of the bridging code. However,
+      this does not mean that they are no longer attached onto their
+      standard IP code hooks. For IP packets that get into
+      contact with the bridging code, the <EM>br-nf</EM> code will
+      decide in which place in the network code the <EM>iptables</EM>
+      chains will be traversed. Obviously, it is guaranteed that no chain is
+      traversed twice by the same packet. All packets that do not come into
+      contact with the bridge code traverse the <EM>iptables</EM> chains
+      in the standard way as seen in Figure 3b.<BR>
+      The following sections try, among other things,
+      to explain what the <EM>br-nf</EM> code does and why it does it.
+    </P>
+    <P>
+      It's possible to see a single IP packet/frame traverse the
+      nat PREROUTING, filter INPUT, nat OUTPUT, filter OUTPUT and
+      nat POSTROUTING <EM>ebtables</EM> chains.<BR>
+      This can happen when the bridge is also used as a router.
+      The Ethernet frame(s) containing that IP packet will have
+      the bridge's destination MAC address, while the destination
+      IP address is not of the bridge. Including the
+      <EM>iptables</EM> chains, this is how the IP packet runs
+      through the bridge/router (actually there is more going on,
+      see <A HREF="#section6">section 6</A>):
+    </P>
+    <P>
+      <IMG SRC="bridge3c.png">
+    </P>
+    <P>
+      <I><B>Figure 3c.</B> Bridge/router routes packet to a
+      bridge interface (simplistic view)</I><BR>
+    </P>
+    <P>
+      This assumes that the routing decision sends the packet to
+      a bridge interface. If the routing decision sends the
+      packet to non-bridge interface, this is what happens:
+    </P>
+    <P>
+      <IMG SRC="bridge3d.png">
+    </P>
+    <P>
+      <I><B>Figure 3d.</B> Bridge/router routes packet to a
+      non-bridge interface (simplistic view)</I><BR>
+    </P>
+    <P>
+      Figures 3c and 3d assume the IP packet arrived on a bridge port.
+      What is obviously "asymmetric" here is that the
+      <EM>iptables</EM> PREROUTING chain is traversed before the
+      <EM>ebtables</EM> INPUT chain, however this cannot be
+      helped without sacrificing functionality. See the
+      next section.
+    </P>
+    <A NAME="section4"></A>
+    <P CLASS="section">
+      4. DNAT'ing bridged packets
+    </P>
+    <P>
+      Take an IP packet received by the bridge. Let's assume we
+      want to do some IP DNAT on it.
+      Changing the destination address of the packet (IP address
+      and MAC address) has to happen before the bridge code
+      decides what to do with the frame/packet.
+    </P>
+    <P>
+      So, this IP DNAT has to happen very early in the bridge
+      code. Namely before the bridge code actually does anything.
+      This is at the same place as where the <EM>ebtables</EM> nat
+      PREROUTING chain will be traversed (for the same reason).
+      This should explain the asymmetry encountered in Figures 3c
+      and 3d.<BR>
+      One should also be aware of the fact that frames for which the
+      bridging decision would be the fourth from the above list (i.e.
+      ignore the frame) will be seen in the PREROUTING chains of
+      <EM>ebtables</EM> and <EM>iptables</EM>.
+    </P>
+    <A NAME="section5"></A>
+    <P CLASS="section">
+      5. Chain traversal for bridged IP packets
+    </P>
+    <P>
+      A bridged packet never enters any network code above layer
+      1 (Link Layer). So, a bridged IP packet/frame will never enter the
+      IP code.
+      Therefore all <EM>iptables</EM> chains will be traversed
+      while the IP packet is in the bridge code. The chain
+      traversal will look like this:
+    </P>
+    <P>
+      <IMG SRC="bridge5.png">
+    </P>
+    <P>
+      <I><B>Figure 5.</B> Chain traversal for bridged IP
+      packets</I><BR>
+    </P>
+    <A NAME="section6"></A>
+    <P CLASS="section">
+      6. Using a bridge port in <EM>iptables</EM> rules
+    </P>
+    <P>
+      The wish to be able to use physical devices belonging to a
+      bridge (bridge ports) in <EM>iptables</EM> rules is valid.
+      Knowing the input bridge ports is necessary to prevent
+      spoofing attacks. Say br0 has ports eth0 and eth1. If
+      <EM>iptables</EM> rules can only use br0 there's no way of
+      knowing when a box on the eth0 side changes its source IP
+      address to that of a box on the eth1 side, except by
+      looking at the MAC source address (and then still...). With
+      the iptables <EM>physdev</EM> module you can use eth0 and eth1 in your
+      <EM>iptables</EM> rules and therefore catch these attempts.
+    </P>
+    <P CLASS="case">
+      6.1. <EM>iptables</EM> wants to use the bridge destination
+      ports:
+    </P>
+    <P>
+      To make this possible the <EM>iptables</EM> chains have to
+      be traversed after the bridge code decided where the frame
+      needs to be sent (eth0, eth1 or both). This has some
+      impact on the scheme presented in <A HREF=
+      "#section3">section 3</A> (so, we are looking at routed
+      traffic here, entering the box on a bridge port). It actually
+      looks like this (in the case of Figure 3c):
+    </P>
+    <P>
+      <IMG SRC="bridge6a.png">
+    </P>
+    <P>
+      <I><B>Figure 6a.</B> Chain traversal for routing, when the bridge
+      and netfilter code are compiled in the kernel.</I><BR>
+    </P>
+    <DIV CLASS="note">
+      All chains are now traversed while in the bridge code.<BR>
+      This is the work of the <EM>br-nf</EM> code. Obviously this does not
+      mean that the routed IP packets never enter the IP code. They
+      just don't pass any <EM>iptables</EM> chains while in the IP code.
+    </DIV>
+    <P CLASS="case">
+      6.2. IP DNAT for locally generated packets (so in the
+      <EM>iptables</EM> nat OUTPUT chain):
+    </P>
+    <P>
+      The normal way locally generated packets would go through
+      the chains looks like this:
+    </P>
+    <P>
+      <IMG SRC="bridge6c.png">
+    </P>
+    <P>
+      <I><B>Figure 6c.</B> The normal way for locally generated
+      packets</I><BR>
+    </P>
+    <P>
+      From <A HREF="#section6">section 6.1</A> we know that this
+      actually looks like this (due to the <EM>br-nf</EM> code):
+    </P>
+    <P>
+      <IMG SRC="bridge6d.png">
+    </P>
+    <P>
+      <I><B>Figure 6d.</B> The actual way for locally generated
+      packets</I><BR>
+    </P>
+    <P>
+      Note that the <EM>iptables</EM> nat OUTPUT chain is traversed while the
+      packet is in the IP code and the <EM>iptables</EM> filter OUTPUT chain
+      is traversed when the packet has passed the bridging decision.
+      This makes it possible to do DNAT to another device in the
+      nat OUTPUT chain and lets us use the bridge ports in the
+      filter OUTPUT chain.
+    </P>
+    <A NAME="section7"></A>
+    <P CLASS="section">
+      7. Two possible ways for frames/packets to pass through the
+      <EM>iptables</EM> PREROUTING, FORWARD and POSTROUTING
+      chains
+    </P>
+    <P>
+      Because of the <EM>br-nf</EM> code, there are 2 ways a frame/packet can
+      pass through the 3 given <EM>iptables</EM> chains. The
+      first way is when the frame is bridged, so the
+      <EM>iptables</EM> chains are called by the bridge code. The
+      second way is when the packet is routed. So special care
+      has to be taken to distinguish between those two,
+      especially in the <EM>iptables</EM> FORWARD chain. Here's
+      an example of strange things to look out for:
+    </P>
+    <P>
+      Consider the following situation
+    </P>
+    <P>
+      <IMG SRC="bridge7a.png">
+    </P>
+    <P>
+      <I><B>Figure 7a.</B> Very basic setup.</I><BR>
+    </P>
+    <P>
+      The default gateway for 172.16.1.2 and
+      172.16.1.4 is 172.16.1.1. 172.16.1.1 is the bridge
+      interface br0 with ports eth1 and eth2.
+    </P>
+    <P CLASS="case">
+      More details:
+    </P>
+    <P>
+      The idea is that traffic between 172.16.1.4 and 172.16.1.2 is
+      bridged, while the rest is routed, using masquerading.
+    </P>
+    <P>
+      <IMG SRC="bridge7b.png">
+    </P>
+    <P>
+      <I><B>Figure 7b.</B> Traffic flow for the example setup.</I><BR>
+    </P>
+    <P>
+      Here's a possible scheme to use at bootup for the bridge/router:
+    </P>
+<PRE>
+iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -d 172.16.1.0/24 -j ACCEPT
+iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j MASQUERADE
+
+brctl addbr br0
+brctl stp br0 off
+brctl addif br0 eth1
+brctl addif br0 eth2
+
+ifconfig eth1 0 0.0.0.0
+ifconfig eth2 0 0.0.0.0
+ifconfig br0 172.16.1.1 netmask 255.255.255.0 up
+
+echo '1' &gt; /proc/sys/net/ipv4/ip_forward
+</PRE>
+    <P>
+      The catch is in the first line. Because the
+      <EM>iptables</EM> code gets executed for both bridged
+      packets and routed packets, we need to make a distinction
+      between the two. We don't really want the bridged frames/packets
+      to be masqueraded. If we omit the first line then
+      everything will work too, but things will happen
+      differently. Let's say 172.16.1.2 pings 172.16.1.4. The
+      bridge receives the ping request and will transmit it
+      through its eth1 port after first masquerading the IP
+      address. So the packet's source IP address will now be
+      172.16.1.1 and 172.16.1.4 will respond to the bridge.
+      Masquerading will change the IP destination of this
+      response from 172.16.1.1 to 172.16.1.4. Everything works
+      fine. But it's better not to have this behaviour. Thus, we
+      use the first line to avoid this. Note that
+      if we would want to filter the connections to and from the
+      Internet, we would certainly need the first line so we don't
+      filter the local connections as well.
+    </P>
+    <A NAME="section8"></A>
+    <P CLASS="section">
+      8. IP DNAT in the <EM>iptables</EM> PREROUTING chain on
+      frames/packets entering on a bridge port
+    </P>
+    <P>
+      Through some groovy play it is assured that (see
+      /net/bridge/br_netfilter.c) DNAT'ed packets that after
+      DNAT'ing have the same output device as the input device
+      they came on (the logical bridge device which we like to
+      call br0) will go through the <EM>ebtables</EM> FORWARD
+      chain, not through the <EM>ebtables</EM> INPUT/OUTPUT chains. All
+      other DNAT'ed packets will be purely routed, so won't go
+      through the <EM>ebtables</EM> FORWARD chain, will go through
+      the <EM>ebtables</EM> INPUT chain and might go through the
+      <EM>ebtables</EM> OUTPUT chain.<BR>
+    </P>
+    <A NAME="section9"></A>
+    <P CLASS="section">
+      9. Using the MAC module extension for <EM>iptables</EM>
+    </P>
+    <P>
+      The side effect explained here occurs when the netfilter code
+      is enabled in the kernel, the IP packet is routed and the
+      out device for that packet is a logical bridge device. The
+      side effect is encountered when filtering on the MAC source
+      in the <EM>iptables</EM> FORWARD chains. As should be clear
+      from earlier sections, the traversal of the
+      <EM>iptables</EM> FORWARD chains is postponed until the
+      packet is in the bridge code. This is done so we can
+      filter on the bridge port out device. This has a side
+      effect on the MAC source address, because the IP code will
+      have changed the MAC source address to the MAC address of
+      the bridge device. It is therefore impossible, in the
+      <EM>iptables</EM> FORWARD chains, to filter on the MAC
+      source address of the computer sending the packet in
+      question to the bridge/router. If you really need to filter
+      on this MAC source address, you should do it in the nat
+      PREROUTING chain. Agreed, very ugly, but making it possible
+      to filter on the real MAC source address in the FORWARD
+      chains would involve a very dirty hack and is probably not
+      worth it. This of course makes the anti-spoofing remark of
+      <A HREF="#section6">section 6</A> funny.
+    </P>
+    <A NAME="section10"></A>
+    <P CLASS="section">
+      10. Using the <EM>iptables</EM> physdev match module for kernel 2.6
+    </P>
+    <P>
+      The 2.6 standard kernel contains an <EM>iptables</EM> match module
+      called <EM>physdev</EM> which has to be used to match the bridge's
+      physical in and out ports. Its basic usage is simple (see the iptables
+      man page for more details):</P>
+      <PRE>iptables -m physdev --physdev-in &lt;bridge-port&gt;</PRE><P>
+      and</P>
+      <PRE>iptables -m physdev --physdev-out &lt;bridge-port&gt;</PRE>
+    </P>
+    <A NAME="section11"></A>
+    <P CLASS="section">
+      11. Detailed IP packet flow
+    </P>
+    <P>
+      Joshua Snyder (&lt;josh_at_imagestream.com&gt;) made a <A HREF="PacketFlow.png" TARGET="_blank">detailed picture</A> about
+      the IP packet flow on a Linux bridging firewall.
+    </P>
+    <HR>
+<PRE>
+Released under the GNU Free Documentation License.
+Copyright (c) 2002-2003 Bart De Schuymer &lt;bdschuym@pandora.be&gt;,
+                        Nick Fedchik &lt;nick@fedchik.org.ua&gt;.
+</PRE>
+    <BR>
+     <BR>
+     <BR>
+     <SMALL>Permission is granted to copy, distribute and/or
+    modify this document under the terms of the GNU Free
+    Documentation License, Version 1.1 or any later version
+    published by the Free Software Foundation, with no Invariant Sections,
+    with no Front-Cover Texts, and with no Back-Cover Texts. For a copy of the
+    license, see <A HREF= "http://www.gnu.org/licenses/fdl.txt">"GNU Free Documentation License"</A>.</SMALL> <BR>
+     <BR>
+    <P>
+      Last updated November 9, 2003.
+    </P>
+  </BODY>
+</HTML>
+
diff --git a/docs/br_fw_ia/bridge2a.png b/docs/br_fw_ia/bridge2a.png
new file mode 100644
index 0000000..8bc23f4
--- /dev/null
+++ b/docs/br_fw_ia/bridge2a.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge2b.png b/docs/br_fw_ia/bridge2b.png
new file mode 100644
index 0000000..b0fff86
--- /dev/null
+++ b/docs/br_fw_ia/bridge2b.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge2c.png b/docs/br_fw_ia/bridge2c.png
new file mode 100644
index 0000000..82007de
--- /dev/null
+++ b/docs/br_fw_ia/bridge2c.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge2d.png b/docs/br_fw_ia/bridge2d.png
new file mode 100644
index 0000000..ca5f085
--- /dev/null
+++ b/docs/br_fw_ia/bridge2d.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge2e.png b/docs/br_fw_ia/bridge2e.png
new file mode 100644
index 0000000..ab0c83f
--- /dev/null
+++ b/docs/br_fw_ia/bridge2e.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge2f.png b/docs/br_fw_ia/bridge2f.png
new file mode 100644
index 0000000..20a3cbb
--- /dev/null
+++ b/docs/br_fw_ia/bridge2f.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge3a.png b/docs/br_fw_ia/bridge3a.png
new file mode 100644
index 0000000..d915b6b
--- /dev/null
+++ b/docs/br_fw_ia/bridge3a.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge3b.png b/docs/br_fw_ia/bridge3b.png
new file mode 100644
index 0000000..584751a
--- /dev/null
+++ b/docs/br_fw_ia/bridge3b.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge3c.png b/docs/br_fw_ia/bridge3c.png
new file mode 100644
index 0000000..75fb782
--- /dev/null
+++ b/docs/br_fw_ia/bridge3c.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge3d.png b/docs/br_fw_ia/bridge3d.png
new file mode 100644
index 0000000..53b4890
--- /dev/null
+++ b/docs/br_fw_ia/bridge3d.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge5.png b/docs/br_fw_ia/bridge5.png
new file mode 100644
index 0000000..9243f40
--- /dev/null
+++ b/docs/br_fw_ia/bridge5.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge6a.png b/docs/br_fw_ia/bridge6a.png
new file mode 100644
index 0000000..7a6233d
--- /dev/null
+++ b/docs/br_fw_ia/bridge6a.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge6c.png b/docs/br_fw_ia/bridge6c.png
new file mode 100644
index 0000000..cc302f1
--- /dev/null
+++ b/docs/br_fw_ia/bridge6c.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge6d.png b/docs/br_fw_ia/bridge6d.png
new file mode 100644
index 0000000..454899d
--- /dev/null
+++ b/docs/br_fw_ia/bridge6d.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge7a.png b/docs/br_fw_ia/bridge7a.png
new file mode 100644
index 0000000..c36bc93
--- /dev/null
+++ b/docs/br_fw_ia/bridge7a.png
Binary files differ
diff --git a/docs/br_fw_ia/bridge7b.png b/docs/br_fw_ia/bridge7b.png
new file mode 100644
index 0000000..c5b5ccd
--- /dev/null
+++ b/docs/br_fw_ia/bridge7b.png
Binary files differ
diff --git a/docs/br_fw_ia/ebtables2.vsd b/docs/br_fw_ia/ebtables2.vsd
new file mode 100644
index 0000000..16df0ba
--- /dev/null
+++ b/docs/br_fw_ia/ebtables2.vsd
Binary files differ
diff --git a/docs/brnf-faq.html b/docs/brnf-faq.html
new file mode 100644
index 0000000..e96a892
--- /dev/null
+++ b/docs/brnf-faq.html
@@ -0,0 +1,166 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<TITLE>Bridge-netfilter Frequently Asked Questions</TITLE>
+	<LINK rel="SHORTCUT ICON" href="">
+	<LINK rel="STYLESHEET" type="text/css" href="brnf.css">
+	<META name="description" content="Bridge-netfilter Frequently Asked Questions">
+	<META name="author" content="Bart De Schuymer">
+	<META name="keywords" content="Linux, netfilter, firewall, bridge, brouter, ebtables, iptables">
+	<META name="keywords" content="FAQ, kernel, ebtables, br-nf, brnf, bridge-nf, ethernet, nat, chains, rules, tables">
+</HEAD>
+<BODY>
+	<DIV class="banner" align="center">
+		<H1>Bridge-netfilter Frequently (and less frequently) Asked Questions</H1>
+	</DIV>
+	<A name="top"></A>
+	<P>Last modified: January 02, 2004</P>
+	<H2>Questions</H2>
+	<OL>
+		<LI class="question"><A href="#quiz0">Connection tracking</A></LI>
+		<LI class="question"><A href="#quiz1">General</A></LI>
+	</OL>
+	<H2>Answers</H2>
+	<OL>
+		<LI class="question">
+			<B><A name="quiz0">Connection tracking</A></B> 
+			<DL>
+				<DT>
+What happens when I enable connection tracking?
+				</DT>
+				<DD>
+By default, all IP packets will be seen by the connection
+tracking code. This code is called on the PF_INET/PRE_ROUTING
+and PF_INET/LOCAL_OUT hooks. For bridged packets, only the
+PRE_ROUTING connection tracking is important.
+				</DD>
+			</DL>
+			<DL>
+				<DT>
+What are the disadvantages of connection tracking on a bridging
+firewall?
+				</DT>
+				<DD>
+					<OL>
+						<LI>
+For an IP packet entering a bridge device, connection tracking
+is called before the bridge code decides what to do with the
+packet. This means that IP packets that will be discarded by
+the bridge code are tracked by connection tracking. For a router,
+the same is true, but a bridge also sees the traffic between
+hosts on the same side of a network. It's possible to prevent
+these packets from being seen by connection tracking: you can
+either drop them in the ebtables nat PREROUTING chain or use the
+iptables NOTRACK target.
+						</LI>
+						<LI>
+Fragmented IP packets (typically UDP traffic like NFS) are
+defragmented by the connection tracking code and refragmented
+before sending them out. This slows down traffic, but the
+transparancy of the firewall isn't diminished.
+						</LI>
+					</OL>
+				</DD>
+			</DL>
+			<A class=navbar href="#top">[Back to the top]</A>
+			<HR>
+		</LI>
+		<LI class="question">
+			<B><A name="quiz1">General</A></B> 
+			<DL>
+				<DT>
+What happens with IP DNAT on a to be bridged packet?
+				</DT>
+				<DD>
+If IP DNAT happened then the bridge-nf code asks the routing
+table where the packet should be sent. If it has to be sent
+over another device (not the bridge device) then the packet is
+routed (an implicit redirect). If the routing table sends the
+packet to the bridge device, then the packet is bridged but the
+MAC destination is correctly changed.
+				</DD>
+			</DL>
+			<DL>
+				<DT>
+How can I disable bridge-nf?
+				</DT>
+				<DD>
+If you don't want iptables and arptables to see bridged traffic,
+you can disable bridge-nf in the 2.6 kernel at compile time by
+disabling "Bridged IP/ARP packets filtering".
+				</DD>
+			</DL>
+			<DL>
+				<DT>
+Can I disable/enable bridge-nf specifics on-the-fly?
+				</DT>
+				<DD>
+As of kernel version 2.6.1, there are three sysctl entries for
+bridge-nf behavioral control (they can be found under
+/proc/sys/net/bridge/):
+					<UL>
+						<LI>
+bridge-nf-call-arptables - pass (1) or don't pass (0) bridged
+ARP traffic to arptables' FORWARD chain.
+						</LI>
+						<LI>
+bridge-nf-call-iptables - pass (1) or don't pass (0) bridged
+IPv4 traffic to iptables' chains.
+						</LI>
+						<LI>
+bridge-nf-filter-vlan-tagged - pass (1) or don't pass (0)
+bridged vlan-tagged ARP/IP traffic to arptables/iptables.
+						</LI>
+					</UL>
+				</DD>
+			</DL>
+			
+			<DL>
+				<DT>
+Do {ip,arp}tables see VLAN tagged IP/ARP traffic on an untagged
+bridge?
+				</DT>
+				<DD>
+Yes. Kernel versions 2.6.0-test7 and above have this
+functionality. For disabling this, see the above question.
+				</DD>
+				<DT>
+How do I let vlan-tagged traffic go through a vlan bridge port
+and the other traffic through a non-vlan bridge port?
+				</DT>
+				<DD>
+Suppose eth0 and eth0.15 are ports of br0. Without countermeasures
+all traffic, including traffic vlan-tagged with tag 15, entering
+the physical device eth0 will go through the bridge port eth0. To
+make the 15-tagged traffic go through the eth0.15 bridge port, use
+the following ebtables rule:
+<PRE>
+ebtables -t broute -A BROUTING -i eth0 --vlan-id 15 -j DROP
+</PRE>
+With the above rule, 15-tagged traffic will enter the bridge on
+the physical device eth0, will then be brouted and enter the
+bridge port eth0.15 after which it is bridged. The packet thus
+enters the BROUTING chain twice, the first time with input
+device eth0 and the second time with input device eth0.15. The
+other chains are only traversed once. All other traffic will
+be bridged with input device eth0.
+				</DD>
+				<DT>
+Do {ip,arp}tables see encapsulated 802.2/802.3 IP/ARP traffic?
+				</DT>
+				<DD>
+No. Adding this shouldn't be that hard though.
+				</DD>
+				<DT>
+Does ip6tables see any bridge IPv6 traffic?
+				</DT>
+				<DD>
+Nope, it's on the todo-list.
+				</DD>
+			</DL>
+			<A class=navbar href="#top">[Back to the top]</A>
+			<HR>
+		</LI>
+	</OL>
+</BODY>
+</HTML>
diff --git a/docs/brnf.css b/docs/brnf.css
new file mode 100644
index 0000000..a99ead0
--- /dev/null
+++ b/docs/brnf.css
@@ -0,0 +1,73 @@
+H1 { FONT: bold 20pt Times, serif; TEXT-ALIGN: center; TEXT-DECORATION: none }
+H2 { font: arial }
+P  {  FONT: 14pt Times, serif }
+LI.question {  FONT: 18pt Times, serif;  margin-top: 5pt; }
+PRE { FONT: 14pt Courier, monospace;
+ margin-top: 5pt;
+ margin-bottom: 5pt;
+ background-color: white;
+ color: black;
+ }
+
+<<<<<<< ebtables.css
+:link { color: #ccff66 }
+:visited { color: #9933ff } 
+=======
+:link { color: #993399 }  
+:visited { color: #6633cc } 
+>>>>>>> 1.2
+:active { color: #0000FF;  } 
+:hover { color: #3300ff;  }
+
+A {
+ text-decoration: none;
+}
+
+.navbar { FONT: 12pt Courier, monospace; font-weight: bolder;  
+ }
+
+.statement { TEXT-DECORATION: underline }
+.section { FONT: bold 22pt Times }
+.case { FONT-STYLE: italic }
+.note {  
+ font-family: Arial;
+ font-weight: normal;
+ font-size: 14pt;
+ padding-left: 0.4em;
+ border: solid;
+ border-width: thin;
+ border-left: solid;
+ border-right: none;
+ border-top: none;
+ border-bottom: none;
+ border-left-width: thin;
+ border-color: red;
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+
+DIV {
+ border: solid;
+ border-width: thin;
+ background-color: #ffcc99
+}
+
+BODY {
+ background-color: white;
+}
+
+DT {
+ color: #ff0033;
+ font-size: 12pt; 
+ font-style: italic;
+ font-weight: bold;
+ font-family: Arial;
+ margin-top: 10pt;
+ margin-bottom: 5pt;
+}
+DD {
+ color: black;
+ font-size: 12pt;
+ font-style: normal;
+ font-family: Helvetica;
+}
diff --git a/docs/ebtables-faq.html b/docs/ebtables-faq.html
new file mode 100644
index 0000000..80e1a97
--- /dev/null
+++ b/docs/ebtables-faq.html
@@ -0,0 +1,264 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+  <HEAD>
+    <TITLE>Ebtables (Ethernet Bridge Tables) Frequently Asked
+    Questions</TITLE>
+    <LINK rel="SHORTCUT ICON" href="">
+    <LINK rel="STYLESHEET" type="text/css" href="ebtables.css">
+    <META name="description" content=
+    "Ethernet Bridge Tables Frequently Asked Questions">
+    <META name="author" content="Bart De Schuymer and Nick Fedchik">
+    <META name="keywords" content=
+    "Linux, netfilter, firewall, bridge, brouter, ebtables, iptables">
+    <META name="keywords" content=
+    "FAQ, kernel, ebtables, br-nf, br-nf-bds, ethernet, nat, chains, rules, tables">
+  </HEAD>
+  <BODY>
+    <DIV class="banner" align="center">
+      <H1>Ebtables (Ethernet Bridge Tables) Frequently Asked Questions</H1>
+    </DIV>
+    <A name="top"></A>
+    <P>Last modified: November 09, 2003</P>
+    <H2>Questions</H2>
+    <OL>
+      <LI><A href="#quiz0">Intro</A></LI>
+      <LI><A href="#quiz1">Installation</A></LI>
+      <LI><A href="#quiz2">Usage</A></LI>
+      <LI><A href="#quiz3">Problems</A></LI>
+      <LI><A href="#quiz4">Other</A></LI>
+    </OL>
+    <H2>Answers</H2>
+    <OL>
+      <LI>
+        <B><A name="quiz0">Intro</A></B> 
+        <DL>
+          <DT>What is ebtables?</DT>
+          <DD>The ebtables project is the Linux 2.5.x (and above) Link Layer
+          firewalling subsystem, a patch for 2.4.x is maintained too.
+          It delivers for Linux the functionality of
+          Ethernet frame filtering, all kinds of frame NAT (Network Address
+          Translation) and frame matching. The ebtables infrastructure is
+          a part of the standard Linux 2.5.x (and above) kernels.</DD>
+          <DT>Why would I use it?</DT>
+          <DD>To filter frames by MAC-address or frame type at
+          Link Layer inside your Linux-based Ethernet bridge, to do
+          some basic filtering of certain protocol headers, to
+          make a Linux brouter.</DD>
+        </DL>
+        <A class=navbar href="#top">[Back to the top]</A>
+        <HR>
+      </LI>
+      <LI>
+        <B><A name="quiz1">Installation</A></B> 
+        <DL>
+          <DT>How do I install the kernel part?</DT>
+          <DD>First step is to decide what kernel version to use. If you
+          want to use a 2.5.x (or above) kernel, then just use the latest
+          and greatest kernel version. You won't have to patch the kernel.
+          </DD>
+          <DD>If you want to use a 2.4.x kernel, then go to
+          <A href="http://sourceforge.net/projects/ebtables/">Ethernet bridge
+          tables</A> and download the latest patch from the <B>2.4-ebtables-brnf</B>
+	  package. Apply the patch as follows (substitute "linux" for the appropriate directory):
+          </DD>
+<PRE>
+# cp ebtables-brnf-3_vs_2.4.22.diff.gz /usr/src/linux
+# cd /usr/src/linux
+# gunzip ebtables-brnf-3_vs_2.4.22.diff.gz
+# patch -p1 &lt; ebtables-brnf-3_vs_2.4.22.diff
+</PRE>
+          </DD>
+          <DT>What is the "ebtables" package and how do I install it?</DT>
+          <DD>
+            The <B>ebtables</B> package contains the ebtables userspace
+            tool. This ebtables binary is used to make filtering
+            rules for the Linux-based Ethernet bridge. All traffic entering
+            or leaving on a bridge port will be seen by the rules. The ebtables usage is very
+            similar to the iptables, so it should not be so hard. Of
+            course, there is a man page supplied. Just gunzip and untar the
+            package and read the INSTALL file.<BR>
+
+<PRE>
+# make
+</PRE>
+            Copy the ebtables binary, man page and protocol file to the correct
+            directory (see the INSTALL file for options):
+<PRE>
+# make install
+</PRE>
+          </DD>
+        </DL>
+        <A class=navbar href="#top">[Back to the top]</A>
+				<HR>
+      </LI>
+      <LI>
+        <B><A name="quiz2">Usage</A></B>
+        <DL>
+          <DT>Can I filter on ARP packets in the Linux bridge box using
+          ebtables?</DT>
+          <DD>Yes, it's possible to filter on the ARP header, using ebtables.
+          See the <A href="http://ebtables.sourceforge.net/ebtables-man.html">ebtables manual page</A> for
+          details.</DD>
+          <DT>Can I use ebtables with iptables? Are there any problems to
+          use it together? How exactly is the packet/frame traversing order for ebtables/iptables?</DT>
+          <DD>Yes, it's possible to use ebtables together with iptables, there are no incompatibility issues. Detailed
+          info about ebtables/iptables interaction is explained at the
+          <A href="http://ebtables.sourceforge.net/br_fw_ia/br_fw_ia.html">
+          "ebtables/iptables interaction on a Linux-based bridge"</A> page.</DD>
+          <DT>Does ebtables keep count statistics?</DT>
+          <DD>
+            Yes, it's possible to view the match and byte count for every rule, using
+<PRE>
+# ebtables -L --Lc
+</PRE>
+          </DD>
+          <DT>When using the option --Lc, what does the pcnt value represent?</DT>
+          <DD>
+          Normally, pcnt will represent the number of frames that matched this rule. However,
+          if IP connection tracking is enabled, all fragmented IP packets will first be
+          defragmented. Therefore, the pcnt value for IP packets will then represent the
+          number of matched IP packets, not the number of matched frames containing IP fragments.
+          In the BROUTING chain however, pcnt will always represent the number of matched frames, since
+          the IP connection tracking is not done before this chain is traversed.
+          </DD>
+          <DT>What is this brouter stuff and when is it useful?</DT>
+          <DD>
+          The ebtables BROUTING chain gets traversed very early, namely right after a frame
+          is received on a forwarding bridge port. If a rule's decision is to route the frame,
+          the input device will remain the physical device of the bridge port and the bridge
+          code won't touch the frame. The frame will be processed by the network stack. If the
+          decision is to bridge the frame (the default behaviour), then the input device will
+          become the bridge device on which the port is enslaved and the bridge code will decide
+          what to do with the frame.</DD>
+          <DT>So, what's the difference between the ebtables BROUTING and PREROUTING chains?</DT>
+          <DD>The ebtables PREROUTING chain is only traversed when the bridge code is deciding what
+          to do with the frame. So, if a BROUTING chain rule decided the frame should be routed, then
+          the ebtables PREROUTING chain won't see it. See the
+          <A href="http://ebtables.sourceforge.net/br_fw_ia/br_fw_ia.html">
+          "ebtables/iptables interaction on a Linux-based bridge"</A> page for the details.</DD>
+          <DT>I'm using a 2.5.x or higher kernel and my iptables rules won't match on the bridge port
+          devices, what's wrong?</DT>
+          <DD>
+          There is one difference between the br-nf behaviour in the 2.5.x or higher kernels and
+          the 2.4.x patch. To get the br-nf code accepted into the standard 2.5.x kernels, we had to
+          remove the code that automatically checked on the bridge port in the iptables port checking
+          code (options -i and -o). Instead there is now an iptables match module, called 'physdev', that
+          can be used to filter on the bridge ports. This match module has some extra options and
+          is in the standard 2.6 kernels, the corresponding userspace module is available in the
+          iptables userspace tool. See the iptables man pages and
+<PRE>
+# iptables -m physdev -h
+</PRE>
+          The kernel module has to be compiled in the kernel, the option 'physdev match support' will
+          appear under the 'IP netfilter configuration' when the bridge is already enabled in the
+          configuration.
+</DD>
+        <DT>I want to use the most recent ebtables code, even if it's not yet in an official release.
+        How do I do this?</DT>
+        <DD>
+        The most recent code is available at the <A href="http://sourceforge.net/projects/ebtables">
+        sourceforge ebtables</A> CVS repository. To get a copy of the repository, do the following:
+<PRE>
+# cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ebtables login
+# cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ebtables co ebtables2
+</PRE>
+        The current userspace code is in the ebtables2/userspace/ebtables2 directory. To compile the
+        CVS userspace tool you'll need to do the following:
+<PRE>
+# make KERNEL_INCLUDES=/usr/src/linux/include/
+# make install
+</PRE>
+        Obviously you'll need to use the right kernel directory.</DD>
+        <DD> Why is compiling the CVS different?
+        Because the kernel include files are not maintained in the userspace directory of the CVS.
+        When a new ebtables release is made, the kernel include files get copied in the tar file, so
+        the standard installation knows where to get its kernel include files.</DD>
+        <DD>The ebtables CVS tree has its own kernel tree with ebtables related files (for 2.4 and 2.6).
+        The CVS directory (base_dir)/ebtables2/kernel/linux2.5/include/ can be used for compiling the userspace tool.
+        </DD>
+        <DD>New ebtables modules might not yet be in the standard kernel. The CVS directory
+        (base_dir)/ebtables2/kernel/linux2.5/net/bridge/netfilter/ contains the not yet submitted modules. The modules
+        that are already in the standard kernel are also in this directory and they are normally in sync with the latest
+        kernel release.
+        </DD>
+        </DL>
+        <BR>
+         <A class=navbar href="#top">[Back to the top]</A>
+      <HR>
+      </LI>
+      <LI>
+        <B><A name="quiz3">Problems</A></B><BR>
+        <DL>
+          <DT>My bridging box seems to drop all IP packets, which is not what I want and I'm sure my
+          ebtables rules don't drop them.</DT>
+          <DD>Your iptables rules are probably dropping them then. By default, on a Linux bridging firewall all
+          bridged IP packets are seen by iptables, so you should take that into account.</DD>
+          <DT>This stuff isn't working on my 64-bit machine with a 32-bit userspace (like the Sparc64)</DT>
+          <DD>As from ebtables v2.0.5, ebtables-brnf-2_vs_2.4.21.diff.gz and above 2.6.0-test1, it should work on a Sparc64.
+          In case it doesn't, please notify the ebtables-devel mailing list. Making it work on a different 64/32 processor
+          should be easy, but we'll wait for someone to come along who asks for this and who can consequently test the fix.</DD>
+          <DT>I'm getting a message that looks like: ``br_netfilter: Argh!! : bad mac.raw pointer''</DT>
+          <DD>We sometimes get reports about this message occurring. The bridge-nf code reports this message when a
+          specific irregularity is observed, in technical terms: the mac.raw pointer of the sk_buff isn't set properly.
+          The most likely cause of this is the network device driver. Since this only happens for a few persons, the
+          only way to debug this is if those persons are willing to try patches. Up until now this has not been the case.<BR>
+          The easiest solution is to try a different type of network card or a different device driver.
+          </DD>
+          <DT>I'm getting this message when doing IP DNAT: ``Performing cross-bridge DNAT requires IP
+          forwarding to be enabled''</DT>
+          <DD>First make sure IP forwarding is enabled:
+<PRE>
+# echo 1 > /proc/sys/net/ipv4/ip_forward
+</PRE>
+          If that's the case and the message doesn't go away, make sure your routing table has all necessary
+          entries. For example, suppose we want to DNAT traffic on a bridge device that doesn't have an IP address to
+          an IP address somewhere on the Internet.
+<PRE>
+eth0 = connection to Internet
+br0 = eth1+eth2
+br0 has no IP address
+iptables -t nat -A PREROUTING -s 172.16.1.2 -d 172.16.1.4 -j DNAT --to-dest &lt;destination&gt;
+route -A -net 172.16.1.0 netmask 255.255.255.0 dev br0
+</PRE>
+        172.16.1.2 is on the eth1 side, .4 on the eth2 side, the &lt;destination&gt; is somewhere on the Internet.
+        Without the routing table entry (last line above), it is obvious that this DNAT wouldn't work (because the bridge/router
+        wouldn't know where to send 172.16.1.xx traffic). It is possible that the mentioned
+        error message gets printed on the screen or in your logs when this routing table entry is omitted.
+        </DD>
+        <DT>I'm trying to create a brouter that routes all IP traffic using the command "ebtables -t broute -A BROUTING -p IPv4 -j DROP", but it's not working...</DT>
+        <DD>The DROP target in the BROUTING chain doesn't change the MAC destination to the bridge device, by default. You need
+        to explicitly do this by using the redirect target:
+<PRE>
+ebtables -t broute -A BROUTING -p IPv4 -j redirect --redirect-target DROP
+</PRE>
+        </DD>
+        </DL>
+        <BR>
+        <A class=navbar href="#top">[Back to the top]</A>
+      </LI>
+      <LI>
+        <B><A name="quiz4">Other</A></B><BR>
+        <DL>
+          <DT>I'm not a Linux system's programmer, but I need a feature, which
+          is not (yet) implemented in ebtables. What should I do?</DT>
+          <DD>Subscribe to the <A href= "https://lists.sourceforge.net/lists/listinfo/ebtables-user">
+          ebtables users mailing list</A>. Then post a short and clean description of
+          your wanted feature to this mailing list.</DD>
+          <DT>I'm a C programmer and I want to add an ebtables feature by
+          myself. Where should I begin?</DT>
+          <DD>Subscribe to the <A href=
+          "https://lists.sourceforge.net/lists/listinfo/ebtables-devel">ebtables
+          developers mail list</A>. Read the <A href=
+          "http://ebtables.sourceforge.net/ebtables-hacking/ebtables-hacking-HOWTO.html">"Ebtables Hacking HOWTO"</A> and
+          have a look at the already implemented modules. You will find that
+          adding a module is not very hard. Additional information is available
+          at the ebtables <A href="http://ebtables.sourceforge.net">
+          homepage</A>.</DD>
+        </DL>
+        <BR>
+        <A class=navbar href="#top">[Back to the top]</A>
+      </LI>
+    </OL>
+    <HR>
+  </BODY>
+</HTML>
diff --git a/docs/ebtables-hacking/ebtables-hacking-HOWTO-1.html b/docs/ebtables-hacking/ebtables-hacking-HOWTO-1.html
new file mode 100644
index 0000000..b4cf2d1
--- /dev/null
+++ b/docs/ebtables-hacking/ebtables-hacking-HOWTO-1.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Ebtables Hacking HOWTO: Introduction</title>
+
+<link HREF="ebtables-hacking-HOWTO.html#toc1" REL=contents>
+</head>
+<body>
+<a HREF="ebtables-hacking-HOWTO-2.html">Next</a>
+Previous
+<a HREF="ebtables-hacking-HOWTO.html#toc1">Contents</a>
+<hr>
+<h2><a NAME="intro"></a> <a NAME="s1">1.</a> <a HREF="ebtables-hacking-HOWTO.html#toc1">Introduction</a></h2>
+
+<p>Hi guys (famous opening sentence).</p>
+
+<p>This document wants to tell the interested how to implement extensions
+on top of the ebtables architecture.</p>
+
+<p>For more understanding of netfilter and a broader look I recommend
+reading the HOWTO's on the netfilter homepage. The "netfilter hacking HOWTO"
+is certainly worth your time. Also very recommended is the
+"ebtables/iptables interaction on a Linux-based bridge" document (call name br_fw_ia) which
+you can find on the ebtables homepage.
+</p>
+<p>
+This document discusses ebtables version 2.0, later versions might have subtle changes.
+</p>
+
+<p>(C) 2002 Bart De Schuymer.  Licenced under the GNU GPL.</p>
+
+<h2><a NAME="ss1.1">1.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc1.1">What is ebtables?</a>
+</h2>
+
+<p>Ebtables is a filter/nat facility for the Linux Ethernet bridge. Its
+implementation and usage is very similar to that of iptables. However,
+ebtables works mostly on the Link Layer, while iptables mostly works on the
+Network Layer.
+<h2><a NAME="ss1.2">1.2</a> <a HREF="netfilter-hacking-HOWTO.html#toc1.2">Why do I need ebtables?</a>
+</h2>
+
+<p>
+Ebtables enables you to get a transparent bridging firewall, it also provides
+the functionality of a brouter and lets you make things like transparent proxys.
+What's cooler than playing around with a firewall? Playing around with a transparent
+firewall (stealth firewall), ofcourse! OK, a really cool stealth firewall would allow
+great stuff like IP NAT; that can be obtained with the bridge-nf stuff, which links
+iptables to the bridging world. For more information about bridge-nf, the br_fw_ia document
+is recommended.
+</p>
+<p>
+Concentrating on ebtables, it enables us, for example, to filter out ugly stuff
+like NetBEUI traffic coming from another side of the bridge into our sweet
+IP-only side. Basically, it gives us complete access to the Ethernet header of all frames
+the bridge can get its hands on, along with some elementary access to the protocols on top
+of Ethernet (like IP and ARP).
+</p>
+
+
+<h2><a NAME="ss1.3">1.3</a> <a HREF="netfilter-hacking-HOWTO.html#toc1.3">Who are you?</a>
+</h2>
+
+<p>I'm just someone who was foolish enough to start reading Rusty's code and, consequently,
+got hooked on kernel hacking. So all blame Rusty!
+</p>
+<hr>
+<a HREF="ebtables-hacking-HOWTO-2.html">Next</a>
+Previous
+<a HREF="ebtables-hacking-HOWTO.html#toc1">Contents</a>
+</body>
+</html>
+
diff --git a/docs/ebtables-hacking/ebtables-hacking-HOWTO-2.html b/docs/ebtables-hacking/ebtables-hacking-HOWTO-2.html
new file mode 100644
index 0000000..5a44731
--- /dev/null
+++ b/docs/ebtables-hacking/ebtables-hacking-HOWTO-2.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Ebtables Hacking HOWTO: Where Can I Get The Latest?</title>
+</head>
+<body>
+<a HREF="ebtables-hacking-HOWTO-3.html">Next</a>
+<a HREF="ebtables-hacking-HOWTO-1.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc2">Contents</a>
+<hr>
+<h2><a NAME="s2">2.</a> <a HREF="netfilter-hacking-HOWTO.html#toc2">Where Can I Get The Latest?</a></h2>
+
+<p>Ebtables has a sourceforge account and uses its CVS services. This CVS contains the latest
+userspace and kernel code along with other documents.
+
+For casual browsing, you can use the
+<a HREF="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/ebtables/">Web Interface</a>.  </p>
+<p>To grab the latest sources, you can do the following:
+<ol>
+<li> Log in to the CVS server anonymously, when prompted for a password simply press the ENTER key:
+<blockquote><code>
+<pre>
+cvs -d:pserver:anonymous@cvs.ebtables.sourceforge.net:/cvsroot/ebtables login
+</pre>
+</code></blockquote>
+</li>
+<li> Check out the ebtables repository:
+<blockquote><code>
+<pre>
+cvs -z3 -d:pserver:anonymous@cvs.ebtables.sourceforge.net:/cvsroot/ebtables co ebtables2
+</pre>
+</code></blockquote>
+</li>
+</ol>
+</p>
+
+<hr>
+<a HREF="ebtables-hacking-HOWTO-3.html">Next</a>
+<a HREF="ebtables-hacking-HOWTO-1.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc2">Contents</a>
+</body>
+</html>
+
diff --git a/docs/ebtables-hacking/ebtables-hacking-HOWTO-3.html b/docs/ebtables-hacking/ebtables-hacking-HOWTO-3.html
new file mode 100644
index 0000000..d80aa2b
--- /dev/null
+++ b/docs/ebtables-hacking/ebtables-hacking-HOWTO-3.html
@@ -0,0 +1,464 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Ebtables Hacking HOWTO: Reference manual</title>
+</head>
+<body>
+<a HREF="ebtables-hacking-HOWTO-4.html">Next</a>
+<a HREF="ebtables-hacking-HOWTO-2.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc3">Contents</a>
+<hr>
+<h2><a NAME="s3">3.</a> <a HREF="netfilter-hacking-HOWTO.html#toc3">Reference manual</a></h2>
+<p>
+This section (claims that it) contains the knowledge necessary to write an extension. For a first time reader
+it's probably less boring to first read the examples section and come back here when necessary.
+</p>
+<h2><a NAME="ss3.1">3.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.1">Userspace</a>
+</h2>
+<p>
+The userspace modules are responsible for putting the user's input into the right form to be given to
+the kernel.
+</p>
+<h3><a NAME="ss3.1.1">3.1.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.1.1">Matches</a>
+</h3>
+<p>
+A match module is a piece of code that looks at frames passing by and decides whether that frame
+matches certain conditions or not. The userspace match is contained in a
+<CODE>struct ebt_u_match</CODE>, of which the fields important to the match implementor will be
+described now:
+<ol>
+<li><CODE>char name[EBT_FUNCTION_MAXNAMELEN]<CODE>
+<p>The name of the match, for example <CODE>ip</CODE>. Try to keep yourself from using capitals.</p>
+</li>
+<li><CODE>unsigned int size</CODE>
+<p>The size of the match data, without the extra padding needed for alignment (this is added by the generic code).</p>
+</li>
+<li><CODE>void (*help)(void)</CODE>
+<p>This function should print out the help information for the match, when the user asks for it
+with the <CODE>-h &#60match&#62</CODE> command. The function can expect a '\n' to have been
+printed right before it is executed and should end with at least one '\n'. The output should
+explain the usage of the module and should look similar to the standard help.
+</p>
+</li>
+<li><CODE>void (*init)(struct ebt_entry_match *m)</CODE>
+<p>This function is executed when the ebtables program starts execution, before any user commands
+are processed. Initializing any private data should be done at this time. The data inside the
+match struct can be initialized too, using the function's argument <CODE>m</CODE>.
+</p>
+</li>
+<li><CODE>int (*parse)(int c, char **argv, int argc,<BR>
+	        const struct ebt_u_entry *entry, unsigned int *flags,<BR>
+	        struct ebt_entry_match **match)<CODE>
+<p>This function parses a user option given on the command line. The function can abort execution
+of the program using ebtables' <CODE>print_error()</CODE> function when appropriate. The return
+value for success is 1, in case of failure it is 0.<br>
+<CODE>c</CODE> contains the option the user used on the command line, <CODE>argv</CODE> and <CODE>argc</CODE>
+are the same two parameters given to the <CODE>main</CODE> function. <CODE>entry</CODE> points to the
+complete new rule that is being constructed. <CODE>flags</CODE> points to an unsigned int private to the
+module that can have any value the module wants. In practice it is used to contain flags for which options
+are already processed. <CODE>match</CODE> points to the data of the match, as you can see it's a double pointer,
+meaning you are allowed to change the address of the match's data (this is done f.e. in ebt_among.c).
+</p>
+</li>
+<li><CODE>void (*final_check)(const struct ebt_u_entry *entry,<BR>
+	   const struct ebt_entry_match *match,<BR>
+	   const char *name, unsigned int hookmask, unsigned int time)</CODE>
+<p>This function is executed after the new rule has been completely parsed without errors. Here you can see
+if there is no invalid use of different options. For example, if the match only works for protocol XyZ, then
+you should check that the user specified <CODE>-p XyZ</CODE>. The <CODE>name</CODE> argument contains the name
+of the table the rule will be put in, <CODE>hookmask</CODE> contains the mask that describes from which base
+chains the rule can be accessed. Because this function can be called twice during the execution of the
+program, the value <CODE>time</CODE> equals 0 for the first execution and 1 for the second. In some situations
+it is necessary to have this knowledge (see <a HREF="#ss3.1.4">section 3.1.4</a> for more information).
+</p>
+</li>
+<li><CODE>void (*print)(const struct ebt_u_entry *entry,<BR>
+	   const struct ebt_entry_match *match)</CODE>
+<p>
+This function is executed when the user wants to list the rules and if a rule contains this match. The output should
+be in a format the user could have used to make the rule, so that the option '--Lx' stays useful.
+</p>
+</li>
+<li><CODE>int (*compare)(const struct ebt_entry_match *m1,<BR>
+	   const struct ebt_entry_match *m2)</CODE>
+<p>
+This function is executed when 2 rules have to be compared with each other and both contain this match. A return value
+of 1 means the matches in both rules are the same, otherwise the return value must be 0.
+</p>
+</li>
+<li><CODE>const struct option *extra_ops</CODE>
+<p>
+This points to a <CODE>struct option</CODE>, recognized by the library function <CODE>getopt_long</CODE>. This contains
+the options the user can use for the match module. Options specific to a match should start with a specific prefix, then
+a minus, then the option specifying name. For example, the IP match has an option "ip-source" for matching the IP source address.
+</p>
+</li>
+</ol>
+</p>
+<h3><a NAME="ss3.1.2">3.1.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.1.2">Watchers</a>
+</h3>
+<p>
+A watcher module is a piece of code that looks at frames passing by, after they have passed all matches of the rule in which the
+watcher is contained. A watcher only looks at a frame and will probably log something or keep statistics. Watchers are in ebtables
+because it allows to have a watcher and a target in the same rule. Therefore you can log stuff with the log watcher, while still
+being able to give a target. Without watchers, you would need two rules for this, which is slower and ugly.<br><br>
+The userspace watcher is contained in a <CODE>struct ebt_u_watcher</CODE> that has the same relevant fields as the match, so we refer
+to the previous section (mentally replace match by watcher where necessary).
+</p>
+<h3><a NAME="ss3.1.3">3.1.3</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.1.3">Targets</a>
+</h3>
+<p>
+A target module is a piece of code that does a certain action when all matches of a rule are passed and after the watchers in
+the rule (if any) are executed.<br><br>
+The userspace target is contained in a <CODE>struct ebt_u_target</CODE> that has the same relevant fields as the match, so we refer
+to the first section (mentally replace match by target where necessary).
+</p>
+<h3><a NAME="ss3.1.4">3.1.4</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.1.4">Miscellaneous</a>
+</h3>
+<p>
+This section contains more general information that you could need.
+</p>
+<p>
+<em>Macro's:</em>
+</p>
+<p>
+The following macro's are defined in <CODE>include/ebtables_u.h</CODE>
+<ol>
+<li>
+<CODE>print_bug()</CODE>
+<p>
+The arguments should look like arguments to <CODE>printf</CODE>, use this macro if you want to do sanity checks on your code.
+</p>
+</li>
+<li>
+<CODE>print_error()</CODE>
+<p>
+The arguments should look like arguments to <CODE>printf</CODE>, use this macro to tell the user she did something wrong.
+A trailing '.' is added by the macro.
+</p>
+</li>
+<li>
+<CODE>print_memory()</CODE>
+<p>
+Use this macro when <CODE>malloc</CODE> and friends fail. No arguments allowed.
+</p>
+</li>
+<li>
+<CODE>FILL_TARGET(string, pos)</CODE>
+<p>
+This macro fills the integer <CODE>pos</CODE> with the value representing the target <CODE>string</CODE>. So, if you want <CODE>pos</CODE> to contain the value for RETURN,
+use <CODE>FILL_TARGET("RETURN", pos);</CODE>
+</p>
+</li>
+<li>
+<CODE>TARGET_NAME(value)</CODE>
+<p>
+This macro produces the target string corresponding to the given target <CODE>value</CODE>. Use this to convert a stored numeric value to a string that can be printed for
+the user to read.
+</p>
+</li>
+<li>
+<CODE>BASE_CHAIN</CODE>
+<p>
+This macro produces a boolean with value true if the rule is in a base chain. This is used for example to prevent a RETURN target on a base chain.
+</p>
+</li>
+<li>
+<CODE>CLEAR_BASE_CHAIN_BIT</CODE>
+<p>
+This macro should be used (only once) before using the <CODE>hookmask</CODE> in the <CODE>final_check()</CODE> function.
+If you want to use <CODE>BASE_CHAIN</CODE> you must use it earlier in the function.
+</p>
+</li>
+</ol>
+</p>
+<p>
+<em>The <CODE>time</CODE> argument to <CODE>final_check()</CODE>:</em>
+</p>
+<p>
+Some extra explanation about the <CODE>time</CODE> argument of the <CODE>final_check()</CODE> function is perhaps needed. When a rule is added, this rule can have as
+target a user defined chain. It can be, for example, that introducing this new rule makes a certain target accessible from a base chain that is not allowed for that target.
+Before this rule was added, this target was not accessible from the base chain, but after the rule was added it is.
+Therefore, after an add or insert, all the <CODE>final_check()</CODE> functions of all
+the modules used in all chains are called, the value of <CODE>time</CODE> will be set to 1. We could of course be lazy and let this checking up to the kernel, but it's the
+policy of ebtables that any rejected table from the kernel is caused by an ebtables userspace bug. Userspace should make sure no invalid data can go to the kernel. This does
+not mean that the kernel no longer has to check for validity, of course.
+</p>
+<p>
+<em>A complete rule:</em>
+</p>
+<p>
+The <CODE>struct ebt_u_entry</CODE> contains the information of a rule. Most module functions that are called by the base ebtables code have this struct as an argument.
+However, I feel there is only one field that can be needed by the userspace module:
+<ol>
+<li><CODE>uint16_t ethproto</CODE>
+<p>
+This contains the Ethernet protocol specified by the user, its value is zero if the user did not specify a specific protocol. The value is in host endian, so there is no need
+for <CODE>ntohs()</CODE>. The <CODE>final_check()</CODE> can need this value to be sure the module is used with the right specified Ethernet protocol.
+</p>
+</li>
+</ol>
+</p>
+<p>
+<em>Adding a new table:</em>
+</p>
+<p>
+A module for a new table can be added too, the table's information is stored in a <CODE>struct ebt_u_table</CODE> and has following relevant members:
+<ol>
+<li><CODE>char name[EBT_TABLE_MAXNAMELEN]</CODE>
+<p>
+The name of the table. Try to keep yourself from using capital letters.
+</p>
+</li>
+<li><CODE>void (*check)(struct ebt_u_replace *repl)</CODE>
+<p>
+This function gets the complete table as an argument and can do all the checks it likes. It should use <CODE>print_error()</CODE> if the table is invalid. However, there
+is probably no need to make this function.
+</p>
+</li>
+<li><CODE>void (*help)(char **)</CODE>
+<p>
+This function gets executed when the user gives the '-h' command, it should at least print out the supported chains for the table. You can assume a '\n' has been printed
+prior to the function's execution, it should also end with at least one '\n'.
+</p>
+</li>
+</ol>
+</p>
+<p>
+<em>Useful functions:</em>
+</p>
+<p>
+Now follows a description of other functions that are useful for a module.
+<ol>
+<li><CODE>void register_table(struct ebt_u_table *)</CODE>
+<p>
+Is needed in the initialization function of a table module, to register the table's data, namely the <CODE>name</CODE> and the <CODE>check</CODE> and <CODE>help</CODE> functions.
+</p>
+</li>
+<li><CODE>void register_match(struct ebt_u_match *)</CODE>
+<p>
+Is needed in the initialization function of a match module, to register its data.
+</p>
+</li>
+<li><CODE>void register_watcher(struct ebt_u_watcher *)</CODE>
+<p>
+Is needed in the initialization function of a watcher module, to register its data.
+</p>
+</li>
+<li><CODE>void register_target(struct ebt_u_target *t)</CODE>
+<p>
+Is needed in the initialization function of a target module, to register its data.
+</p>
+</li>
+<li><CODE>struct ethertypeent *getethertypebyname(const char *name)</CODE>
+<p>
+Translate a name of an Ethernet protocol to the corresponding protocol number. The
+translation is done using /etc/ethertypes.
+</p>
+</li>
+<li><CODE>struct ethertypeent *getethertypebynumber(int type)</CODE>
+<p>
+Translate a protocol number to a protocol name, using /etc/ethertypes.
+</p>
+</li>
+<li><CODE>void check_option(unsigned int *flags, unsigned int mask)</CODE>
+<p>
+Checks the boolean <CODE>(*flags & mask)</CODE>. Normally, <CODE>mask</CODE> should be a power of 2 and <CODE>flags</CODE> points to an integer containing
+all the options already processed for the specific module. If the boolean is true then an error message is printed to the screen about double usage
+of the same option and the program exits. If the boolean is false, the bit in <CODE>*flags</CODE> for the specific option is put to 1.
+</p>
+</li>
+<li><CODE>int check_inverse(const char option[])</CODE>
+<p>
+Checks if the string argument equals "!". If it does, <CODE>optind</CODE> is increased and 1 is returned, else 0 is returned. As this function can increase <CODE>optind</CODE>,
+the code that comes behind the call to this function has to use <CODE>argv[optind - 1]</CODE>, not <CODE>optarg</CODE> because <CODE>optarg</CODE> can point to the "!".
+</p>
+</li>
+</ol>
+</p>
+<p>
+<em>The <CODE>hookmask</CODE> argument to <CODE>final_check()</CODE>:</em>
+</p>
+<p>
+The usage of the <CODE>hookmask</CODE> argument for the <CODE>final_check()</CODE> function could use some extra explaining. This mask contains the information from which
+base chain the rule in question (so the module's data in question) can be reached. If your module is only viable for certain base chains, you should check it isn't used
+in an invalid chain. If the rule can be reached through the PREROUTING chain, then the NF_BR_PRE_ROUTING'th least significant bit will be set for <CODE>hookmask</CODE>. Here
+is a list of all constants: NF_BR_PRE_ROUTING = 0, NF_BR_LOCAL_IN = 1, NF_BR_FORWARD = 2, NF_BR_LOCAL_OUT = 3, NF_BR_POST_ROUTING = 4, NF_BR_BROUTING = 5. See the already
+implemented modules for examples.
+</p>
+<h2><a NAME="ss3.2">3.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.2">Kernel</a>
+</h2>
+<p>
+The kernel modules are responsible for checking the data received by userspace and its main task is doing whatever the
+data tells the module to do, with a frame.
+</p>
+<h3><a NAME="ss3.2.1">3.2.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.2.1">Matches</a>
+</h3>
+<p>
+The kernel match module is contained in a <CODE>struct ebt_match</CODE> of which its relevant fields will be discussed now:
+<ol>
+<li><CODE>char name[EBT_FUNCTION_MAXNAMELEN]</CODE>
+<p>
+The name of the match, should be the same as the name of the corresponding userspace match.
+</p></li>
+<li><CODE>int (*match)(const struct sk_buff *skb, const struct net_device *in,<BR>
+                       const struct net_device *out, const void *matchdata,<BR>
+                       unsigned int datalen)</CODE>
+<p>
+Checks if the frame (<CODE>skb</CODE>) matches the data from the match (<CODE>matchdata</CODE>) contained in the rule. If it matches,
+EBT_MATCH (=0) is returned, else EBT_NOMATCH (=1) is returned. This function is executed for every frame that
+comes into contact with a rule utilizing the match in question.
+</p></li>
+<li><CODE>int (*check)(const char *tablename, unsigned int hookmask,<BR>
+                       const struct ebt_entry *e, void *matchdata, unsigned int datalen)</CODE>
+<p>
+Checks the data of the match (<CODE>matchdata</CODE>) contained in the rule (<CODE>e</CODE>) to see if it is valid. This function is executed
+when the user gives new table data to the kernel. Returns 0 on success, a negative value (e.g. <CODE>-EINVAL</CODE>) on failure.
+</p></li>
+<li><CODE>void (*destroy)(void *matchdata, unsigned int datalen)</CODE>
+<p>
+Contains the code executed when a rule utilizing the match is removed. Set to <CODE>NULL</CODE> if not used.
+</p></li>
+<li><CODE>struct module *me</CODE>
+<p>
+Always set to <CODE>THIS_MODULE</CODE>.
+</p>
+</li>
+</ol>
+</p>
+<h3><a NAME="ss3.2.2">3.2.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.2.2">Watchers</a>
+</h3>
+<p>
+The watchers are contained in a <CODE>struct ebt_watcher</CODE>, its members are basically the same as
+for the <CODE>struct ebt_match</CODE>, except that the <CODE>watcher()</CODE> function
+(which is analogous to the <CODE>match()</CODE> function) has no return value.
+</p>
+<h3><a NAME="ss3.2.3">3.2.3</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.2.3">Targets</a>
+</h3>
+<p>
+The targets are contained in a <CODE>struct ebt_target</CODE>, its members are basically the same as
+for the <CODE>struct ebt_match</CODE>, except that it contains a <CODE>target()</CODE> member instead of a <CODE>match()</CODE> member.
+This <CODE>target()</CODE> function gives back a basic decision to the main ebtables
+code. This decision is <CODE>EBT_ACCEPT</CODE>, <CODE>EBT_DROP</CODE>, <CODE>EBT_CONTINUE</CODE> or <CODE>EBT_RETURN</CODE>.
+The <CODE>target()</CODE> function should make sure the decision cannot be <CODE>EBT_RETURN</CODE> for a rule on a base chain.
+</p>
+<h3><a NAME="ss3.2.4">3.2.4</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.2.4">Miscellaneous</a>
+</h3>
+<p>
+<em>Macros:</em>
+</p>
+<p>
+Some macros useful to ebtables kernel modules:
+<ol>
+<li><CODE>FWINV(bool,invflg)</CODE>
+<p>
+Used in the <CODE>match()</CODE> functions. The <CODE>bool</CODE> argument contains some boolean expression
+and the <CODE>invflg</CODE> argument has the bit set for a specific option. See the implementation in <CODE>ebtables.h</CODE> and
+the usage in f.e. <CODE>ebt_ip.c</CODE> for more details.
+</p>
+</li>
+<li>
+<CODE>BASE_CHAIN</CODE>
+<p>
+True if the hook mask denotes that the rule is in a base chain,
+used in the <CODE>check()</CODE> functions.
+</p>
+</li>
+<li><CODE>CLEAR_BASE_CHAIN_BIT</CODE>
+<p>
+This macro should be used (only once) before using the <CODE>hookmask</CODE> in the <CODE>check()</CODE> function.
+If you want to use <CODE>BASE_CHAIN</CODE> you must use it earlier in the function.
+See f.e. <CODE>ebt_dnat.c</CODE>.
+</p>
+</li>
+<li><CODE>INVALID_TARGET</CODE>
+<p>
+True if the target (an integer) is not a standard target. See f.e. <CODE>ebt_dnat.c</CODE> for its usage.
+</p>
+</li>
+</ol>
+</p>
+<p>
+<em>Adding a new table:</em>
+</p>
+<p>
+You can also make a new table, see the existing implementations (e.g. <CODE>ebtable_filter.c</CODE>) for details.
+</p>
+<p>
+<em>Useful functions:</em>
+</p>
+<p>
+<ol>
+<li><CODE>int ebt_register_table(struct ebt_table *table)</CODE>
+<p>
+Registers the table with its data contained in the <CODE>struct ebt_table *table</CODE>. Returns a negative value on failure.
+This is used in the initialization function of a table module.
+</p>
+</li>
+<li><CODE>void ebt_unregister_table(struct ebt_table *table)</CODE>
+<p>
+Unregisters the table. This is used in the function that is called when the module is unloaded and also when something goes wrong
+in the initialization function.
+</p>
+</li>
+<li><CODE>int ebt_register_match(struct ebt_match *match)</CODE>
+<p>
+Registers the match with its data contained in the <CODE>struct ebt_match *match</CODE>. Returns a negative value on failure.
+This is used in the initialization function of a match module.
+</p>
+</li>
+<li><CODE>void ebt_unregister_match(struct ebt_match *match)</CODE>
+<p>
+Unregisters the match. This is used in the function that is called when the module is unloaded and also when something goes wrong
+in the initialization function.
+</p>
+</li>
+<li><CODE>int ebt_register_watcher(struct ebt_watcher *watcher)</CODE>
+<p>
+Registers the watcher with its data contained in the <CODE>struct ebt_watcher *watcher</CODE>. Returns a negative value on failure.
+This is used in the initialization function of a watcher module.
+</p>
+</li>
+<li><CODE>void ebt_unregister_watcher(struct ebt_watcher *watcher)</CODE>
+<p>
+Unregisters the watcher. This is used in the function that is called when the module is unloaded and also when something goes wrong
+in the initialization function.
+</p>
+</li>
+<li><CODE>int ebt_register_target(struct ebt_target *target)</CODE>
+<p>
+Registers the target with its data contained in the <CODE>struct ebt_target *target</CODE>. Returns a negative value on failure.
+This is used in the initialization function of a target module.
+</p>
+</li>
+<li><CODE>void ebt_unregister_target(struct ebt_target *target)</CODE>
+<p>
+Unregisters the target. This is used in the function that is called when the module is unloaded and also when something goes wrong
+in the initialization function.
+</p>
+</li>
+</ol>
+</p>
+<h2><a NAME="ss3.3">3.3</a> <a HREF="ebtables-hacking-HOWTO.html#toc3.3">General rules</a>
+</h2>
+<p>
+<ol>
+<li>
+Indentation should be done using tabs.
+</li>
+<li>
+No more than 80 columns should be used, taking into account tab width of 8 characters.
+</li>
+<li>
+Bad or corrupt data should never be accepted by the kernel module's <CODE>check()</CODE> function. The userspace module should be
+built so that no bad or corrupt data can ever be given to the kernel. If the kernel does not accept data given to it, this is considered
+a userspace bug.
+</li>
+</ol>
+</p>
+<hr>
+<a HREF="ebtables-hacking-HOWTO-4.html">Next</a>
+<a HREF="ebtables-hacking-HOWTO-2.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc3">Contents</a>
+</body>
+</html>
diff --git a/docs/ebtables-hacking/ebtables-hacking-HOWTO-4.html b/docs/ebtables-hacking/ebtables-hacking-HOWTO-4.html
new file mode 100644
index 0000000..39f7ac7
--- /dev/null
+++ b/docs/ebtables-hacking/ebtables-hacking-HOWTO-4.html
@@ -0,0 +1,637 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<title>Ebtables Hacking HOWTO: Examples</title>
+</head>
+<body>
+<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
+<hr>
+<h2><a NAME="s4">4.</a> <a HREF="netfilter-hacking-HOWTO.html#toc4">Example</a></h2>
+<p>
+This section contains annotated example source code. This is just to give some clues, see the full code of the already implemented ebtables modules
+for more clues.
+</p>
+<h2><a NAME="ss4.1">4.1</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.1">Userspace</a></h2></p>
+<p>
+<em><h3>The userspace ip match module</h3></em>
+</p>
+<p>
+What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the <CODE>--ip-source</CODE> option is considered.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+#include &lt;stdio.h&gt;
+#include &lt;getopt.h&gt;
+#include "../include/ebtables_u.h"
+#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+At least these includes are needed: respectively for <CODE>printf()</CODE>, for the parsing of the options, for all the
+general ebtables userspace definitions and for the ebtables ip module specific header.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+#define IP_SOURCE '1'
+#define IP_DEST   '2'
+
+static struct option opts[] =
+{
+	{ "ip-source"     , required_argument, 0, IP_SOURCE },
+	{ "ip-src"        , required_argument, 0, IP_SOURCE },
+	{ 0 }
+};
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Defining the new command line options. This is a struct that the library function <CODE>getopt_long()</CODE>
+understands. The first field specifies the full length option name, the second field specifies if an argument is required
+or not, the third field should be zero and the last field specifies the value returned by <CODE>getopt_long()</CODE> when
+it encounters this option.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void print_help()
+{
+	printf(
+"ip options:\n"
+"--ip-src    [!] address[/mask]: ip source specification\n"
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Short and descriptive help.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
+	ipinfo->invflags = 0;
+	ipinfo->bitmask = 0;
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Initialize the module specific data. In the example, some fields in the struct specific to the ip match module are given an initial value.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+#define OPT_SOURCE 0x01
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		check_option(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP_SOURCE;
+
+		if (check_inverse(optarg))
+			ipinfo->invflags |= EBT_IP_SOURCE;
+
+		if (optind > argc)
+			print_error("Missing IP address argument");
+		parse_ip_address(argv[optind - 1], &ipinfo->saddr, &ipinfo->smsk);
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This function parses the option, filling in the match specific struct or exiting on input errors. <CODE>check_option()</CODE> makes sure the option isn't
+specified twice on the command line. <CODE>check_inverse()</CODE> checks if the argument equals '!'. <CODE>optind &gt; argc</CODE> is possible if the command
+line input ends with '--ip-source !'.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
+		print_error("For IP filtering the protocol must be "
+		            "specified as IPv4");
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This function is executed for a rule that uses the (ip) module. When there is an error, it exits using <CODE>print_error()</CODE>.
+<CODE>entry->ethproto</CODE> contains the protocol specified by the user. If the protocol wasn't specified, this field will equal zero. The specified protocol is
+in host endian form, so there is no need for <CODE>ntohs()</CODE>.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+	int j;
+
+	if (ipinfo->bitmask & EBT_IP_SOURCE) {
+		printf("--ip-src ");
+		if (ipinfo->invflags & EBT_IP_SOURCE)
+			printf("! ");
+		for (j = 0; j < 4; j++)
+			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
+			   (j == 3) ? "" : ".");
+		printf("%s ", mask_to_dotted(ipinfo->smsk));
+	}
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Print out the data contained in the match in a form that the user could have used on the command line. End with at least one space.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
+	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
+
+	if (ipinfo1->bitmask != ipinfo2->bitmask)
+		return 0;
+	if (ipinfo1->invflags != ipinfo2->invflags)
+		return 0;
+	if (ipinfo1->bitmask & EBT_IP_SOURCE) {
+		if (ipinfo1->saddr != ipinfo2->saddr)
+			return 0;
+		if (ipinfo1->smsk != ipinfo2->smsk)
+			return 0;
+	}
+	return 1;
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Compares the data of two "instances" of the match module, returning 1 if the data is equivalent, else 0.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct ebt_u_match ip_match =
+{
+	.name		= EBT_IP_MATCH,
+	.size		= sizeof(struct ebt_ip_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The struct used to register the match to the core ebtables userspace code.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void _init(void) __attribute((constructor));
+static void _init(void)
+{
+	register_match(&ip_match);
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The init function registers the match. Initializing its own data should be done with the <CODE>init()</CODE> function defined int the above struct.
+</p>
+<p>
+<em><h3>The (userspace) dnat <CODE>final_check()</CODE> function</h3></em>
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void final_check_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+		print_error("--dnat-target RETURN not allowed on base chain");
+	CLEAR_BASE_CHAIN_BIT;
+	if (((hookmask & ~((1 &lt;&lt; NF_BR_PRE_ROUTING) | (1 &lt;&lt; NF_BR_LOCAL_OUT)))
+	   || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 &lt;&lt; NF_BR_BROUTING)) || strcmp(name, "broute")))
+		print_error("Wrong chain for dnat");
+	if (time == 0 && to_dest_supplied == 0)
+		print_error("No dnat address supplied");
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+First we check that this target isn't RETURN on one of the standard (base) chains. Then we make
+<CODE>hookmask</CODE> ready for direct use by using the <CODE>CLEAR_BASE_CHAIN_BIT</CODE> macro. Next is checked if the rule containing this
+"module instance" is accessible through illegal chains or tables.
+Finally, the argument <CODE>time</CODE> is checked. If it equals zero, the function checks to be sure a destination IP address was specified.
+<CODE>to_dest_supplied</CODE> is a static variable of this ebtables userspace module, initialized to zero by its initialization function.
+</p>
+</h2>
+<h2><a NAME="ss4.2">4.2</a> <a HREF="ebtables-hacking-HOWTO.html#toc4.2">Kernel</a>
+</h2>
+<p>
+<em><h3>The ebtables kernel ip match module</h3></em>
+</p>
+<p>
+What follows is annotated code of pieces of the <CODE>ebt_ip.c</CODE> code. For brevity, only the IP source address filtering is considered.
+As the interface towards the main ebtables code is easy, there is really not much to be said here.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+#include &lt;linux/netfilter_bridge/ebtables.h&gt;
+#include &lt;linux/netfilter_bridge/ebt_ip.h&gt;
+#include &lt;linux/ip.h&gt;
+#include &lt;linux/module.h&gt;
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+We need general ebtables definitions, the ip kernel match module's specific data, the definition of the IP header and some module definitions.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+	union {struct iphdr iph; struct tcpudphdr ports;} u;
+
+	if (skb_copy_bits(skb, 0, &u.iph, sizeof(u.iph)))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_IP_SOURCE &&
+	   FWINV((u.iph.saddr & info->smsk) !=
+	   info->saddr, EBT_IP_SOURCE))
+		return EBT_NOMATCH;
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This is the filtering function of the ip match module, it is executed for every
+frame that comes into contact with an ebtables rule that uses the ip match. All it does
+is tell the ebtables main code if the frame matches or not.<br>
+As the data isn't necessarily linearized in memory, meaning that the data isn't
+guaranteed to be in consecutive memory places, we need to use skb_copy_bits()
+to copy the IP header to the stack. We can then match the data on the stack.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+	if (datalen != sizeof(struct ebt_ip_info))
+		return -EINVAL;
+	if (e->ethproto != __constant_htons(ETH_P_IP) ||
+	   e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+		return -EINVAL;
+	return 0;
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This function is executed for every rule that uses the ip match, when the kernel receives new table data. It needs to make sure no corrupt ip match data is accepted.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct ebt_match filter_ip =
+{
+	.name		= EBT_IP_MATCH,
+	.match		= ebt_filter_ip,
+	.check		= ebt_ip_check,
+	.me		= THIS_MODULE,
+};
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The struct we'll give to the main ebtables code to register the match.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int __init init(void)
+{
+	return ebt_register_match(&filter_ip);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_ip);
+}
+
+module_init(init);
+module_exit(fini);
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Functions executed at loading or removing of the ip match module.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+MODULE_LICENSE("GPL");
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Of course your module is released under the GPL.
+</p>
+<p>
+<em><h3>The ebtables kernel filter table</h3></em>
+</p>
+<p>
+This part contains pieces of the <CODE>ebtable_filter.c</CODE> code with annotation.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+#define FILTER_VALID_HOOKS ((1 &lt;&lt; NF_BR_LOCAL_IN) | (1 &lt;&lt; NF_BR_FORWARD) | \
+   (1 &lt;&lt; NF_BR_LOCAL_OUT))
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The valid netfilter hooks for the ebtables filter table are the bridge <CODE>LOCAL_IN</CODE> <CODE>FORWARD</CODE> and <CODE>LOCAL_OUT</CODE> hooks.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct ebt_entries initial_chains[] =
+{
+	{
+		.name	= "INPUT",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "FORWARD",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "OUTPUT",
+		.policy	= EBT_ACCEPT,
+	},
+};
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The filter table consists of three chains, initially containing zero rules and having policy <CODE>ACCEPT</CODE>.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct ebt_replace initial_table =
+{
+	.name		= "filter",
+	.valid_hooks	= FILTER_VALID_HOOKS,
+	.entries_size	= 3 * sizeof(struct ebt_entries),
+	.hook_entry	= {
+		[NF_BR_LOCAL_IN]	= &initial_chains[0],
+		[NF_BR_FORWARD]		= &initial_chains[1],
+		[NF_BR_LOCAL_OUT]	= &initial_chains[2],
+	},
+	.entries	= (char *)initial_chains,
+};
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This contains all the info about the table.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~FILTER_VALID_HOOKS)
+		return -EINVAL;
+	return 0;
+}
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This function is executed when new table data is given to the kernel. We just check that
+the valid hooks according to userspace are the same as those according to the kernel module.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct ebt_table frame_filter =
+{ 
+	.name		= "filter",
+	.table		= &initial_table,
+	.valid_hooks	= FILTER_VALID_HOOKS, 
+	.lock		= RW_LOCK_UNLOCKED,
+	.check		= check,
+	.me		= THIS_MODULE,
+};
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+The ebtables main code likes to use this struct.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static unsigned int
+ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
+   const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_filter);
+}
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+This function is executed for every frame that passes through a netfilter hook on which this function is registered.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static struct nf_hook_ops ebt_ops_filter[] = {
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_LOCAL_IN,
+		.priority	= NF_BR_PRI_FILTER_BRIDGED,
+	},
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_FORWARD,
+		.priority	= NF_BR_PRI_FILTER_BRIDGED,
+	},
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_LOCAL_OUT,
+		.priority	= NF_BR_PRI_FILTER_OTHER,
+	},
+};
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Here we say the function <CODE>ebt_hook()</CODE> is registered onto the three mentioned netfilter hooks, with a certain priority (e.g. NF_BR_PRI_FILTER_BRIDGED).
+If multiple functions are registered on the same hook, the priority of the functions determines the order in which they are executed. The lower the priority value,
+the earlier the function will get executed.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static int __init init(void)
+{
+	int i, j, ret;
+
+	ret = ebt_register_table(&frame_filter);
+	if (ret &lt; 0)
+		return ret;
+	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
+		if ((ret = nf_register_hook(&ebt_ops_filter[i])) &lt; 0)
+			goto cleanup;
+	return ret;
+cleanup:
+	for (j = 0; j &lt; i; j++)
+		nf_unregister_hook(&ebt_ops_filter[j]);
+	ebt_unregister_table(&frame_filter);
+	return ret;
+}
+
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Register the table to the main ebtables code; register <CODE>ebt_hook()</CODE> on the appropriate netfilter hooks.
+</p>
+<p>
+<table BGCOLOR="#E0E0E0" WIDTH="100%">
+<tr><td>
+<PRE>
+static void __exit fini(void)
+{
+	int i;
+
+	for (i = 0; i &lt; ARRAY_SIZE(ebt_ops_filter); i++)
+		nf_unregister_hook(&ebt_ops_filter[i]);
+	ebt_unregister_table(&frame_filter);
+}
+</PRE>
+</td></tr>
+</table>
+</p>
+<p>
+Unregister from the netfilter hooks and ebtables.
+</p>
+<hr>
+Next
+<a HREF="ebtables-hacking-HOWTO-3.html">Previous</a>
+<a HREF="ebtables-hacking-HOWTO.html#toc4">Contents</a>
+</body>
+</html>
diff --git a/docs/ebtables-hacking/ebtables-hacking-HOWTO.html b/docs/ebtables-hacking/ebtables-hacking-HOWTO.html
new file mode 100644
index 0000000..2e51142
--- /dev/null
+++ b/docs/ebtables-hacking/ebtables-hacking-HOWTO.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+  <HEAD>
+    <TITLE>Ebtables Hacking HOWTO</TITLE>
+  </HEAD>
+  <BODY>
+    <A href="ebtables-hacking-HOWTO-1.html">Next</A> Previous Contents 
+    <HR>
+    <H1>Ebtables Hacking HOWTO</H1>
+    <H2>Bart De Schuymer,<BR>
+    mailing lists <CODE>ebtables-devel@lists.sourceforge.net</CODE> and
+    <CODE>ebtables-user@lists.sourceforge.net</CODE></H2>
+    Last updated: November 11, 2003
+    <HR>
+    <EM>This document describes the ebtables v2.0.x architecture for Linux
+    and how to implement new modules on top of it.</EM> 
+    <HR>
+    <H2><A name="toc1">1.</A> <A href=
+    "ebtables-hacking-HOWTO-1.html">Introduction</A></H2>
+    <UL>
+      <LI><A name="toc1.1">1.1</A> <A href= 
+      "ebtables-hacking-HOWTO-1.html#ss1.1">What is ebtables?</A></LI>
+      <LI><A name="toc1.2">1.2</A> <A href= 
+      "ebtables-hacking-HOWTO-1.html#ss1.2">Why do I need
+      ebtables?</A></LI>
+      <LI><A name="toc1.3">1.3</A> <A href= 
+      "ebtables-hacking-HOWTO-1.html#ss1.3">Who are you?</A></LI>
+    </UL>
+    <H2><A name="toc2">2.</A> <A href="ebtables-hacking-HOWTO-2.html">Where
+    Can I Get The Latest?</A></H2>
+    <H2><A name="toc3">3.</A> <A href=
+    "ebtables-hacking-HOWTO-3.html">Reference manual</A></H2>
+    <UL>
+      <LI>
+        <A name="toc3.1">3.1</A> <A href= 
+        "ebtables-hacking-HOWTO-3.html#ss3.1">Userspace</A> 
+        <UL>
+          <LI><A name="toc3.1.1">3.1.1</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.1.1">Matches</A></LI>
+          <LI><A name="toc3.1.2">3.1.2</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.1.2">Watchers</A></LI>
+          <LI><A name="toc3.1.3">3.1.3</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.1.3">Targets</A></LI>
+          <LI><A name="toc3.1.4">3.1.4</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.1.4">Miscellaneous</A></LI>
+        </UL>
+      </LI>
+      <LI>
+        <A name="toc3.2">3.2</A> <A href= 
+        "ebtables-hacking-HOWTO-3.html#ss3.2">Kernel</A> 
+        <UL>
+          <LI><A name="toc3.2.1">3.2.1</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.2.1">Matches</A></LI>
+          <LI><A name="toc3.2.2">3.2.2</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.2.2">Watchers</A></LI>
+          <LI><A name="toc3.2.3">3.2.3</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.2.3">Targets</A></LI>
+          <LI><A name="toc3.2.4">3.2.4</A> <A href= 
+          "ebtables-hacking-HOWTO-3.html#ss3.2.4">Miscellaneous</A></LI>
+        </UL>
+      </LI>
+      <LI><A name="toc3.3">3.3</A> <A href= 
+      "ebtables-hacking-HOWTO-3.html#ss3.3">General rules</A></LI>
+    </UL>
+    <H2><A name="toc4">4.</A> <A href=
+    "ebtables-hacking-HOWTO-4.html">Examples</A></H2>
+    <UL>
+      <LI><A name="toc4.1">4.1</A> <A href= 
+      "ebtables-hacking-HOWTO-4.html#ss4.1">Userspace</A></LI>
+      <LI><A name="toc4.2">4.2</A> <A href= 
+      "ebtables-hacking-HOWTO-4.html#ss4.2">Kernel</A></LI>
+    </UL>
+    <BR>
+    <HR>
+    <A href="netfilter-hacking-HOWTO-1.html">Next</A> Previous Contents
+  </BODY>
+</HTML>
diff --git a/docs/ebtables.css b/docs/ebtables.css
new file mode 100644
index 0000000..65a8efd
--- /dev/null
+++ b/docs/ebtables.css
@@ -0,0 +1,68 @@
+H1 { FONT: bold 20pt Times, serif; TEXT-ALIGN: center; TEXT-DECORATION: none }
+H2 { font: arial }
+P  {  FONT: 14pt Times, serif }
+LI {  FONT: 18pt Times, serif;  margin-top: 5pt; }
+PRE { FONT: 14pt Courier, monospace;
+ margin-top: 5pt;
+ margin-bottom: 5pt;
+ background-color: white;
+ color: black;
+ }
+
+:link { color: #993399 }  
+:visited { color: #6633cc } 
+:active { color: #0000FF;  } 
+:hover { color: #3300ff;  }
+
+A {
+ text-decoration: none;
+}
+
+.navbar { FONT: 12pt Courier, monospace; font-weight: bolder;  
+ }
+
+.statement { TEXT-DECORATION: underline }
+.section { FONT: bold 22pt Times }
+.case { FONT-STYLE: italic }
+.note {  
+ font-family: Arial;
+ font-weight: normal;
+ font-size: 14pt;
+ padding-left: 0.4em;
+ border: solid;
+ border-width: thin;
+ border-left: solid;
+ border-right: none;
+ border-top: none;
+ border-bottom: none;
+ border-left-width: thin;
+ border-color: red;
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+
+DIV {
+ border: solid;
+ border-width: thin;
+ background-color: #ffcc99
+}
+
+BODY {
+ background-color: white;
+}
+
+DT {
+ color: #ff0033;
+ font-size: 12pt; 
+ font-style: italic;
+ font-weight: bold;
+ font-family: Arial;
+ margin-top: 10pt;
+ margin-bottom: 5pt;
+}
+DD {
+ color: black;
+ font-size: 12pt;
+ font-style: normal;
+ font-family: Helvetica;
+}
\ No newline at end of file
diff --git a/docs/how_it_works.html b/docs/how_it_works.html
new file mode 100755
index 0000000..dcf85c0
--- /dev/null
+++ b/docs/how_it_works.html
@@ -0,0 +1,266 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
+<HTML><HEAD><TITLE>How bridge/ebtables/iptables interaction works</TITLE>
+<META http-equiv=Content-Type content="text/html; charset=iso-8859-15">
+<STYLE type=text/css>H1 {
+	FONT: bold 25pt Times, serif; TEXT-ALIGN: center; TEXT-DECORATION: underline
+}
+P {
+	FONT: 20pt Times, serif
+}
+LI {
+	MARGIN-BOTTOM: 2em; FONT: 22pt 'Times New Roman', serif
+}
+PRE {
+	FONT: 18pt Courier, monospace
+}
+.statement {
+	TEXT-DECORATION: underline
+}
+.section {
+	FONT: bold 22pt Times
+}
+.case {
+	FONT-STYLE: italic
+}
+</STYLE>
+
+<META content="MSHTML 6.00.2505.0" name=GENERATOR></HEAD>
+<BODY>
+<H1>How bridge/ebtables/iptables interaction works</H1>
+
+<P class=section>1. How frames traverse the <EM>ebtables</EM> chains:</P>
+<P>This section only considers <EM>ebtables</EM>, _not_ <EM>iptables</EM>.</P>
+<PRE>
+     Route
+       ^
+       |
+I  +--------+ Bridge  +----------+                     +-------+      +-----------+   O
+N->|BROUTING|-------->|PREROUTING|----->[BRIDGING]---->|FORWARD| ---->|POSTROUTING|-->U
+   +--------+         +----------+      [DECISION]     +-------+      +-----------+   T
+                                             |                              ^ 
+                                             v                              |
+                                          +-----+                      +----------+
+                                          |INPUT|                      |OUTPUT (2)|
+                                          +-----+                      +----------+
+                                             |                              ^
+                                             |                              |
+                                             |                         +----------+
+                                             |                         +OUTPUT (1)+
+                                             |                         +----------+
+                                             |                              ^
+                                             +------->Local Process---------+
+</PRE>
+<P>
+First thing to keep in mind is that we are talking about the ethernet layer here,
+so the OSI layer 2. A packet destined for the local computer according to the bridge
+(which works on the ethernet layer) isn't necessarily destined for the local computer
+according to the ip layer. That's how routing works (MAC destination is the router, ip
+destination is the actual box you want to communicate with).</P>
+<P>
+<EM>Ebtables</EM> currently has three tables: filter, nat and broute. The filter table has a
+FORWARD, INPUT and OUTPUT chain. The nat table has a PREROUTING, OUTPUT and POSTROUTING chain.
+The broute table has the BROUTING chain. In the figure the filter OUTPUT chain has (2)
+appended and the nat OUTPUT chain has (1) appended. So these two OUTPUT chains are not
+the same (and have a different intended use).</P>
+<P>
+When a nic enslaved to a bridge receives a frame, the frame will first go through the BROUTING
+chain. In this special chain one can choose whether to route or bridge frames. The default
+is bridging and we will assume the decision in this chain is 'bridge'. So, next the frame
+passes through the PREROUTING chain. This chain is intended for you to be able to alter the
+destination MAC address of
+frames (DNAT). If the frame passes this chain, the bridging code will decide where the
+frame should be sent. The bridge does this by looking at the destination MAC address, it
+doesn't care about the OSI layer 3 addresses (e.g. ip address). Note that frames coming in
+on non-forwarding ports of a bridge will not be seen by <EM>ebtables</EM>, not even by the BROUTING
+chain.</P>
+<P>
+If the bridge decides the frame is for the bridging computer, the frame will go through the
+INPUT chain. In this chain you can filter frames destined for the bridge box. After passing
+the INPUT chain, the frame will be given to the code on layer 3 (i.e. it will be passed up),
+e.g. to the ip code. So, a routed ip packet will go through the <EM>ebtables</EM> INPUT chain, not
+through the <EM>ebtables</EM> FORWARD chain. This is logical.</P>
+<P>
+Else the frame should possibly be sent onto another side of the bridge. If it should, the
+frame will go through the FORWARD chain and the POSTROUTING chain. In the FORWARD chain one
+can filter frames that will be bridged, the POSTROUTING chain is intended to be able to
+change the MAC source address (SNAT).</P>
+<P>
+Frames that originate from the bridge box itself will go, after the bridging decision, through the
+nat OUTPUT chain, through the filter OUTPUT chain and the POSTROUTING chain. The
+nat OUTPUT chain allows you to alter the destination MAC address and the filter OUTPUT chain
+allows you to filter frames originating from the bridge box. Note that the nat OUTPUT chain is
+traversed after the bridging decision, so actually too late. We should change this. The POSTROUTING
+chain is the same one as described above. Note that it is also possible for routed frames to go
+through these chains, this is when the destination device is a logical bridge device.</P>
+<P class=section>
+2. A machine used as a bridge and a router (not a brouter):</P>
+<P>
+It's possible to see a single ip packet pass the PREROUTING, INPUT, nat OUTPUT, filter OUTPUT
+and POSTROUTING <EM>ebtables</EM> chains.</P>
+<P>
+This can happen when the bridge is also used as a router. The ethernet frame(s) containing that
+ip packet will have the bridge's destination MAC address, while the destination ip address is not
+that of the bridge. Including the <EM>iptables</EM> chains, this is how the ip packet runs through the
+bridge/router (eb=ebtables , ip=iptables ):</P>
+<PRE>ebPREROUTING->ipPREROUTING->ebINPUT->ipFORWARD->ipPOSTROUTING->ebOUTPUT(1)->ebOUTPUT(2)->ebPOSTROUTING->send packet</PRE>
+<P>
+This assumes that the routing decision sends the packet to a bridge interface. If the routing
+decision sends the packet to a physical network card, this is what happens:</P>
+<PRE>ebPREROUTING->ipPREROUTING->ebINPUT->ipFORWARD->ipPOSTROUTING->send packet</PRE>
+<P>
+What is obviously "asymmetric" here is that the <EM>iptables</EM> PREROUTING chain is traversed before
+the <EM>ebtables</EM> INPUT chain, however this can not be helped. See the next section.</P>
+<P class=section>
+3. DNATing bridged packets:</P>
+<P>
+Take an ip packet received by the bridge,  it enters the bridge code. Lets assume we want to do
+some ip DNAT on it. Changing the destination address of the packet (ip address and MAC address)
+has to happen before the bridge code decides what to do with the packet. The bridge code can decide
+to bridge it (if the destination MAC address is on another side of the bridge), flood it over all
+the forwarding bridge ports (the position of the box with the destination MAC is unknown to the bridge),
+give it to the higher protocol code (here, the ip code) if the destination MAC address is that of the
+bridge, or ignore it (the destination MAC address is located on the same side of the bridge).</P>
+<P>
+So, this ip DNAT has to happen very early in the bridge code. Namely before the bridge code
+actually does anything. This is at the same place as where the <EM>ebtables</EM> PREROUTING chain will
+be traversed (for the same reason).</P>
+<P class=section>
+4. Chain traversal for bridged ip packets:</P>
+<P>
+A bridged packet never enters any network code above layer 2. So a bridged ip packet will never
+enter the ip code. Therefore all <EM>iptables</EM> chains will be traversed while the ip packet is in the
+bridge code. The chain traversal will look like this:</P>
+<PRE>
+ebPREROUTING->ipPREROUTING->ebFORWARD->ipFORWARD->ebPOSTROUTING->ipPOSTROUTING</PRE>
+<P>
+Once again note that there is a certain form of asymmetry here that cannot be helped.</P>
+<P class=section>
+5. Using a bridge port in <EM>iptables</EM> rules:</P>
+<P>
+The wish to be able to use physical devices belonging to a bridge (bridge ports) in <EM>iptables</EM> rules
+is valid. It's necessary to prevent spoofing attacks. Say br0 has ports eth0 and eth1. If <EM>iptables</EM>
+rules can only use br0 there's no way of knowing when a box on the eth0 side changes it's source ip
+address to that of a box on the eth1 side, except by looking at the MAC source address (and then
+still...). With the current bridge/iptables patch (0.0.6 or later) you can use eth0 and eth1 in your
+<EM>iptables</EM> rules and therefore catch these attempts.</P>
+<P class=case>
+1. <EM>iptables</EM> wants to use bridge ports:<P>
+<P>
+To make this possible the <EM>iptables</EM> chains have to be traversed after the bridge code decided where
+the frame needs to be sent (eth0, eth1, both or none). This has some impact on the scheme presented
+in section 2 (so, we are looking at routed traffic here). It actually looks like this:</P>
+<PRE>
+ebPREROUTING->ipPREROUTING->ebINPUT->ipFORWARD->ebOUTPUT(1)->ebOUTPUT(2)->ipPOSTROUTING->ebPOSTROUTING->send packet</PRE>
+<P>
+Note that this is the work of the br-nf patch. If one does not compile the br-nf code into the kernel,
+the chains will be traversed as shown below. However, then one can only use br0, not eth0/eth1 to
+filter.</P>
+<PRE>ebPREROUTING->ebINPUT->ipPREROUTING->ipFORWARD->ipPOSTROUTING->ebOUTPUT(1)->ebOUTPUT(2)->ebPOSTROUTING->send packet</PRE>
+<P>
+Notice that ipPREROUTING is now in the natural position in the chain list and too far to be able to change
+the bridging decision. More precise: ipPREROUTING is now traversed while the packet is in the ip code.</P>
+<P class=case>
+2. IP DNAT for locally generated packets (so in the <EM>iptables</EM> nat OUTPUT chain):</P>
+<P>
+The 'normal' way locally generated packets would go through the chains looks like this:</P>
+<PRE>
+ipOUTPUT(1)->ipOUTPUT(2)->ipPOSTROUTING->ebOUTPUT(1)->ebOUTPUT(2)->ebPOSTROUTING</PRE>
+<P>
+From the section 5.1 we know that this actually looks like this:</P>
+<PRE>
+ipOUTPUT(1)->ipOUTPUT(2)->ebOUTPUT(1)->ebOUTPUT(2)->ebPOSTROUTING->ipPOSTROUTING</PRE>
+<P>
+Here we denote by ipOUTPUT(1) (resp. ipOUTPUT(2)) the <EM>iptables</EM> nat (resp. filter) OUTPUT chain. Note that
+the ipOUTPUT(1) chain is traversed while the packet is in the ip code, while the ipOUTPUT(2) chain is traversed when
+the packet has entered the bridge code. This makes it possible to do DNAT to another device in ipOUTPUT(1) and lets
+one use the bridge ports in the ipOUTPUT(2) chain.</P>
+<P class=section>
+6. Two possible ways for frames/packets to pass through the <EM>iptables</EM> PREROUTING, FORWARD and POSTROUTING
+chains:</P>
+<P>
+With the br-nf patch there are 2 ways a frame/packet can pass through the 3 given <EM>iptables</EM>
+chains. The first way is when the frame is bridged, so the <EM>iptables</EM> chains are called by the bridge code.
+The second way is when the packet is routed. So special care has to be taken to distinguish between those
+two, especially in the <EM>iptables</EM> FORWARD chain. Here's an example of strange things to look out for:</P>
+<P>
+Consider the following situation (my personal setup)</P>
+<PRE>
+         +-----------------+
+         |   cable modem   |
+         +-------+---------+
+                 |
+                 |
+             eth0|IP via DHCP from ISP
+         +-------+---------+
+         |bridge/router/fw |
+         +--+-----------+--+
+        eth1| 172.16.1.1|eth2
+            |   (br0)   |
+            |           |
+  172.16.1.4|           |172.16.1.2
+ +----------+---+    +--+------------+
+ |test computer/|    |    desktop    |
+ |backup server |    +---------------+
+ +--------------+</PRE>
+<P>
+With this setup I can test the bridge+ebtables+iptables code while having access to the internet from all
+three computers. The default gateway for 172.16.1.2 and 172.16.1.4 is 172.16.1.1. 172.16.1.1 is the bridge
+interface br0 with ports eth1 and eth2.</P>
+<P class=case>More details:</P>
+<P>
+The idea is that traffic between 172.16.1.4 and 172.16.2 is bridged, while the rest is routed, using
+masquerading. Here's the "script" I use at bootup for the bridge/router:</P>
+<PRE>
+iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -d 172.16.1.0/24 -j ACCEPT
+iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -j MASQUERADE
+insmod ebtables
+insmod ebtable_filter
+insmod ebtable_nat
+insmod ebt_nat
+insmod ebt_log
+insmod ebt_arp
+insmod ebt_ip
+insmod br_db
+brctl addbr br0
+brctl stp br0 off
+brctl addif br0 eth1
+brctl addif br0 eth2
+ifconfig eth1 0 0.0.0.0
+ifconfig eth2 0 0.0.0.0
+ifconfig br0 172.16.1.1 netmask 255.255.255.0 up
+echo '1' > /proc/sys/net/ipv4/ip_forward</PRE>
+<P>
+The catch is in the first line. Because the <EM>iptables</EM> code gets executed for both bridged packets and routed
+packets we need to make a distinction between the two. We don't really want the bridged packets to be
+masqueraded. If we omit the first line then everything will work too, but things will happen differently.
+Let's say 172.16.1.2 pings 172.16.1.4. The bridge receives the ping request and will transmit it through its eth1
+port after first masquerading the ip address. So the packet's source ip address will now be 172.16.1.1 and
+172.16.1.4 will respond to the bridge. Masquerading will change the ip destination of this response from
+172.16.1.1 to 172.16.1.4. Everything works fine. But it's better not to have this behaviour. Thus, we use the
+first line of the script to avoid this. Note that if I wanted to filter the connections to and from the
+internet, I would certainly need the first line so I don't filter the local connections as well.</P>
+<P class=section>
+7. ip DNAT in the <EM>iptables</EM> PREROUTING chain on frames/packets entering on a bridge port:</P>
+<P>Through some groovy play it is assured that (see /net/bridge/br_netfilter.c) DNAT'ed packets that after DNAT'ing
+have the same output device as the input device they came on (the logical bridge device which we like to call br0)
+will be bridged, not routed. So they will go through the <EM>ebtables</EM> FORWARD chain. All other DNAT'ed packets will be
+routed, so won't go through the <EM>ebtables</EM> FORWARD chain, will go through the <EM>ebtables</EM> INPUT chain and might go
+through the <EM>ebtables</EM> OUTPUT chain.</P>
+<P class=section>
+8. using the mac module extension for <EM>iptables</EM>:</P>
+<P>The side effect explained here occurs when the br-nf code is compiled in the kernel, the ip packet is routed and the out device
+for that packet is a logical bridge. The side effect is encountered when filtering on the mac source in the
+<EM>iptables</EM> FORWARD chains. As should be clear from earlier sections, the traversal of the <EM>iptables</EM> FORWARD chains
+is postponed until the packet is in the bridge code. This is done so one can filter on the bridge port out device. This has a
+side effect on the MAC source address, because the ip code will have changed the MAC source address to the MAC address of the bridge.
+It is therefore impossible, in the <EM>iptables</EM> FORWARD chains, to filter on the MAC source address of the computer sending
+the packet in question to the bridge/router. If you really need to filter on this MAC source address, you should do it in the nat
+PREROUTING chain. Agreed, very ugly, but making it possible to filter on the real MAC source address in the FORWARD chains would
+involve a very dirty hack and is probably not worth it.</P>
+<P>
+Released under the GPL.</P>
+<P>
+Bart De Schuymer.</P>
+<P>
+Last updated June 2nd, 2002.</P>
+</BODY></HTML>
diff --git a/kernel/README b/kernel/README
new file mode 100644
index 0000000..91eeb99
--- /dev/null
+++ b/kernel/README
@@ -0,0 +1,50 @@
+---
+Here are the source code and patches for the kernel files ebtables
+changes/creates.
+
+Bart De Schuymer,
+June 1, 2002
+---
+All kernel code in linux/ is tagged like this example:
+ebtables_kernel-2-0-pre7
+
+June 1, 2002
+---
+Release candidates are tagged like this:
+ebtables_kernel-2-0-rc1
+
+July 31, 2002
+---
+Added the linux2.5 directory, which contains files that are different
+between the 2.4 patch (linux/) and the 2.5 patch (linux2.5).
+Added the br-nf-bds2.5 directory, containing the different files for
+the 2.5 patch
+The repository should now contain files compilable against 2.4.20-pre4 and
+2.5.31.
+
+August 23, 2002
+---
+Release candidates for 2.5 are tagged like this:
+ebtables_kernel-2-0-dev-rc2
+
+August 30, 2002
+---
+
+Stable releases are tagged like this:
+ebtables_kernel-2-0
+
+September 19, 2002
+---
+
+The repository contains files compilable against 2.4.20-pre5 and 2.5.35.
+
+September 19, 2002
+---
+
+Further releases like this:
+ebtables_kernel-2-0-001
+
+October 17, 2002
+---
+
+
diff --git a/kernel/linux/include/linux/if_bridge.h b/kernel/linux/include/linux/if_bridge.h
new file mode 100644
index 0000000..0bd48c1
--- /dev/null
+++ b/kernel/linux/include/linux/if_bridge.h
@@ -0,0 +1,110 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: if_bridge.h,v 1.3 2002/09/16 21:50:18 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_BRIDGE_H
+#define _LINUX_IF_BRIDGE_H
+
+#include <linux/types.h>
+
+#define BRCTL_VERSION 1
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+#define BRCTL_SET_BRIDGE_HELLO_TIME 9
+#define BRCTL_SET_BRIDGE_MAX_AGE 10
+#define BRCTL_SET_AGEING_TIME 11
+#define BRCTL_SET_GC_INTERVAL 12
+#define BRCTL_GET_PORT_INFO 13
+#define BRCTL_SET_BRIDGE_STP_STATE 14
+#define BRCTL_SET_BRIDGE_PRIORITY 15
+#define BRCTL_SET_PORT_PRIORITY 16
+#define BRCTL_SET_PATH_COST 17
+#define BRCTL_GET_FDB_ENTRIES 18
+
+#define BR_STATE_DISABLED 0
+#define BR_STATE_LISTENING 1
+#define BR_STATE_LEARNING 2
+#define BR_STATE_FORWARDING 3
+#define BR_STATE_BLOCKING 4
+
+struct __bridge_info
+{
+	__u64 designated_root;
+	__u64 bridge_id;
+	__u32 root_path_cost;
+	__u32 max_age;
+	__u32 hello_time;
+	__u32 forward_delay;
+	__u32 bridge_max_age;
+	__u32 bridge_hello_time;
+	__u32 bridge_forward_delay;
+	__u8 topology_change;
+	__u8 topology_change_detected;
+	__u8 root_port;
+	__u8 stp_enabled;
+	__u32 ageing_time;
+	__u32 gc_interval;
+	__u32 hello_timer_value;
+	__u32 tcn_timer_value;
+	__u32 topology_change_timer_value;
+	__u32 gc_timer_value;
+};
+
+struct __port_info
+{
+	__u64 designated_root;
+	__u64 designated_bridge;
+	__u16 port_id;
+	__u16 designated_port;
+	__u32 path_cost;
+	__u32 designated_cost;
+	__u8 state;
+	__u8 top_change_ack;
+	__u8 config_pending;
+	__u8 unused0;
+	__u32 message_age_timer_value;
+	__u32 forward_delay_timer_value;
+	__u32 hold_timer_value;
+};
+
+struct __fdb_entry
+{
+	__u8 mac_addr[6];
+	__u8 port_no;
+	__u8 is_local;
+	__u32 ageing_timer_value;
+	__u32 unused;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/netdevice.h>
+
+struct net_bridge;
+struct net_bridge_port;
+
+extern int (*br_ioctl_hook)(unsigned long arg);
+extern int (*br_handle_frame_hook)(struct sk_buff *skb);
+extern int (*br_should_route_hook)(struct sk_buff **pskb);
+
+#endif
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge.h b/kernel/linux/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..3c271c6
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge.h
@@ -0,0 +1,35 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter. 
+ */
+
+#include <linux/config.h>
+#include <linux/netfilter.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING	4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING		5
+#define NF_BR_NUMHOOKS		6
+
+enum nf_br_hook_priorities {
+	NF_BR_PRI_FIRST = INT_MIN,
+	NF_BR_PRI_FILTER_BRIDGED = -200,
+	NF_BR_PRI_FILTER_OTHER = 200,
+	NF_BR_PRI_NAT_DST_BRIDGED = -300,
+	NF_BR_PRI_NAT_DST_OTHER = 100,
+	NF_BR_PRI_NAT_SRC = 300,
+	NF_BR_PRI_LAST = INT_MAX,
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_802_3.h b/kernel/linux/include/linux/netfilter_bridge/ebt_802_3.h
new file mode 100644
index 0000000..b3d6c32
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_802_3.h
@@ -0,0 +1,60 @@
+#ifndef __LINUX_BRIDGE_EBT_802_3_H
+#define __LINUX_BRIDGE_EBT_802_3_H
+
+#define EBT_802_3_SAP 0x01
+#define EBT_802_3_TYPE 0x02
+
+#define EBT_802_3_MATCH "802_3"
+
+/*
+ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
+ * to discover what kind of packet we're carrying. 
+ */
+#define CHECK_TYPE 0xaa
+
+/*
+ * Control field may be one or two bytes.  If the first byte has
+ * the value 0x03 then the entire length is one byte, otherwise it is two.
+ * One byte controls are used in Unnumbered Information frames.
+ * Two byte controls are used in Numbered Information frames.
+ */
+#define IS_UI 0x03
+
+#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
+
+/* ui has one byte ctrl, ni has two */
+struct hdr_ui {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint8_t ctrl;
+	uint8_t orig[3];
+	uint16_t type;
+};
+
+struct hdr_ni {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint16_t ctrl;
+	uint8_t  orig[3];
+	uint16_t type;
+};
+
+struct ebt_802_3_hdr {
+	uint8_t  daddr[6];
+	uint8_t  saddr[6];
+	uint16_t len;
+	union {
+		struct hdr_ui ui;
+		struct hdr_ni ni;
+	} llc;
+};
+
+struct ebt_802_3_info 
+{
+	uint8_t  sap;
+	uint16_t type;
+	uint8_t  bitmask;
+	uint8_t  invflags;
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_among.h b/kernel/linux/include/linux/netfilter_bridge/ebt_among.h
new file mode 100644
index 0000000..307c1fe
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_among.h
@@ -0,0 +1,65 @@
+#ifndef __LINUX_BRIDGE_EBT_AMONG_H
+#define __LINUX_BRIDGE_EBT_AMONG_H
+
+#define EBT_AMONG_DST 0x01
+#define EBT_AMONG_SRC 0x02
+
+/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
+ * 
+ * Write-once-read-many hash table, used for checking if a given
+ * MAC address belongs to a set or not and possibly for checking
+ * if it is related with a given IPv4 address.
+ *
+ * The hash value of an address is its last byte.
+ * 
+ * In real-world ethernet addresses, values of the last byte are
+ * evenly distributed and there is no need to consider other bytes.
+ * It would only slow the routines down.
+ *
+ * For MAC address comparison speedup reasons, we introduce a trick.
+ * MAC address is mapped onto an array of two 32-bit integers.
+ * This pair of integers is compared with MAC addresses in the
+ * hash table, which are stored also in form of pairs of integers
+ * (in `cmp' array). This is quick as it requires only two elementary
+ * number comparisons in worst case. Further, we take advantage of
+ * fact that entropy of 3 last bytes of address is larger than entropy
+ * of 3 first bytes. So first we compare 4 last bytes of addresses and
+ * if they are the same we compare 2 first.
+ *
+ * Yes, it is a memory overhead, but in 2003 AD, who cares?
+ */
+
+struct ebt_mac_wormhash_tuple
+{
+	uint32_t cmp[2];
+	uint32_t ip;
+};
+
+struct ebt_mac_wormhash
+{
+	int table[257];
+	int poolsize;
+	struct ebt_mac_wormhash_tuple pool[0];
+};
+
+#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
+		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
+
+struct ebt_among_info
+{
+	int wh_dst_ofs;
+	int wh_src_ofs;
+	int bitmask;
+};
+
+#define EBT_AMONG_DST_NEG 0x1
+#define EBT_AMONG_SRC_NEG 0x2
+
+#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
+#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
+
+#define EBT_AMONG_MATCH "among"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h b/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h
new file mode 100644
index 0000000..537ec6b
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h
@@ -0,0 +1,32 @@
+#ifndef __LINUX_BRIDGE_EBT_ARP_H
+#define __LINUX_BRIDGE_EBT_ARP_H
+
+#define EBT_ARP_OPCODE 0x01
+#define EBT_ARP_HTYPE 0x02
+#define EBT_ARP_PTYPE 0x04
+#define EBT_ARP_SRC_IP 0x08
+#define EBT_ARP_DST_IP 0x10
+#define EBT_ARP_SRC_MAC 0x20
+#define EBT_ARP_DST_MAC 0x40
+#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
+   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
+#define EBT_ARP_MATCH "arp"
+
+struct ebt_arp_info
+{
+	uint16_t htype;
+	uint16_t ptype;
+	uint16_t opcode;
+	uint32_t saddr;
+	uint32_t smsk;
+	uint32_t daddr;
+	uint32_t dmsk;
+	unsigned char smaddr[ETH_ALEN];
+	unsigned char smmsk[ETH_ALEN];
+	unsigned char dmaddr[ETH_ALEN];
+	unsigned char dmmsk[ETH_ALEN];
+	uint8_t  bitmask;
+	uint8_t  invflags;
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_arpreply.h b/kernel/linux/include/linux/netfilter_bridge/ebt_arpreply.h
new file mode 100644
index 0000000..96a8339
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_arpreply.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
+#define __LINUX_BRIDGE_EBT_ARPREPLY_H
+
+struct ebt_arpreply_info
+{
+	unsigned char mac[ETH_ALEN];
+	int target;
+};
+#define EBT_ARPREPLY_TARGET "arpreply"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h b/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h
new file mode 100644
index 0000000..d370d7b
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_inat.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+struct ebt_inat_tuple
+{
+	int enabled;
+	unsigned char mac[ETH_ALEN];
+	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+	int target;
+};
+
+struct ebt_inat_info
+{
+	uint32_t ip_subnet;
+	struct ebt_inat_tuple a[256];
+	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
+	int target;
+};
+
+#define EBT_ISNAT_TARGET "isnat"
+#define EBT_IDNAT_TARGET "idnat"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h b/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h
new file mode 100644
index 0000000..499089b
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h
@@ -0,0 +1,43 @@
+/*
+ *  ebt_ip
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP_H
+#define __LINUX_BRIDGE_EBT_IP_H
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#define EBT_IP_PROTO 0x08
+#define EBT_IP_SPORT 0x10
+#define EBT_IP_DPORT 0x20
+#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
+ EBT_IP_SPORT | EBT_IP_DPORT )
+#define EBT_IP_MATCH "ip"
+
+// the same values are used for the invflags
+struct ebt_ip_info
+{
+	uint32_t saddr;
+	uint32_t daddr;
+	uint32_t smsk;
+	uint32_t dmsk;
+	uint8_t  tos;
+	uint8_t  protocol;
+	uint8_t  bitmask;
+	uint8_t  invflags;
+	uint16_t sport[2];
+	uint16_t dport[2];
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_limit.h b/kernel/linux/include/linux/netfilter_bridge/ebt_limit.h
new file mode 100644
index 0000000..d8b6500
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_limit.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
+#define __LINUX_BRIDGE_EBT_LIMIT_H
+
+#define EBT_LIMIT_MATCH "limit"
+
+/* timings are in milliseconds. */
+#define EBT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+struct ebt_limit_info
+{
+	u_int32_t avg;    /* Average secs between packets * scale */
+	u_int32_t burst;  /* Period multiplier for upper limit. */
+
+	/* Used internally by the kernel */
+	unsigned long prev;
+	u_int32_t credit;
+	u_int32_t credit_cap, cost;
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_log.h b/kernel/linux/include/linux/netfilter_bridge/ebt_log.h
new file mode 100644
index 0000000..d3e7377
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_log.h
@@ -0,0 +1,17 @@
+#ifndef __LINUX_BRIDGE_EBT_LOG_H
+#define __LINUX_BRIDGE_EBT_LOG_H
+
+#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
+#define EBT_LOG_ARP 0x02
+#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
+#define EBT_LOG_PREFIX_SIZE 30
+#define EBT_LOG_WATCHER "log"
+
+struct ebt_log_info
+{
+	uint8_t loglevel;
+	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
+	uint32_t bitmask;
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_mark_m.h b/kernel/linux/include/linux/netfilter_bridge/ebt_mark_m.h
new file mode 100644
index 0000000..301524f
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_mark_m.h
@@ -0,0 +1,15 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
+#define __LINUX_BRIDGE_EBT_MARK_M_H
+
+#define EBT_MARK_AND 0x01
+#define EBT_MARK_OR 0x02
+#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
+struct ebt_mark_m_info
+{
+	unsigned long mark, mask;
+	uint8_t invert;
+	uint8_t bitmask;
+};
+#define EBT_MARK_MATCH "mark_m"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_mark_t.h b/kernel/linux/include/linux/netfilter_bridge/ebt_mark_t.h
new file mode 100644
index 0000000..f84d2ad
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_mark_t.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
+#define __LINUX_BRIDGE_EBT_MARK_T_H
+
+struct ebt_mark_t_info
+{
+	unsigned long mark;
+	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+	int target;
+};
+#define EBT_MARK_TARGET "mark"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h
new file mode 100644
index 0000000..eac1871
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_nat.h
@@ -0,0 +1,13 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+struct ebt_nat_info
+{
+	unsigned char mac[ETH_ALEN];
+	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
+	int target;
+};
+#define EBT_SNAT_TARGET "snat"
+#define EBT_DNAT_TARGET "dnat"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_pkttype.h b/kernel/linux/include/linux/netfilter_bridge/ebt_pkttype.h
new file mode 100644
index 0000000..0d64bbb
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_pkttype.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
+#define __LINUX_BRIDGE_EBT_PKTTYPE_H
+
+struct ebt_pkttype_info
+{
+	uint8_t pkt_type;
+	uint8_t invert;
+};
+#define EBT_PKTTYPE_MATCH "pkttype"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h
new file mode 100644
index 0000000..c741521
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
+#define __LINUX_BRIDGE_EBT_REDIRECT_H
+
+struct ebt_redirect_info
+{
+	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+	int target;
+};
+#define EBT_REDIRECT_TARGET "redirect"
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_stp.h b/kernel/linux/include/linux/netfilter_bridge/ebt_stp.h
new file mode 100644
index 0000000..e5fd678
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_stp.h
@@ -0,0 +1,46 @@
+#ifndef __LINUX_BRIDGE_EBT_STP_H
+#define __LINUX_BRIDGE_EBT_STP_H
+
+#define EBT_STP_TYPE		0x0001
+
+#define EBT_STP_FLAGS		0x0002
+#define EBT_STP_ROOTPRIO	0x0004
+#define EBT_STP_ROOTADDR	0x0008
+#define EBT_STP_ROOTCOST	0x0010
+#define EBT_STP_SENDERPRIO	0x0020
+#define EBT_STP_SENDERADDR	0x0040
+#define EBT_STP_PORT		0x0080
+#define EBT_STP_MSGAGE		0x0100
+#define EBT_STP_MAXAGE		0x0200
+#define EBT_STP_HELLOTIME	0x0400
+#define EBT_STP_FWDD		0x0800
+
+#define EBT_STP_MASK		0x0fff
+#define EBT_STP_CONFIG_MASK	0x0ffe
+
+#define EBT_STP_MATCH "stp"
+
+struct ebt_stp_config_info
+{
+	uint8_t flags;
+	uint16_t root_priol, root_priou;
+	char root_addr[6], root_addrmsk[6];
+	uint32_t root_costl, root_costu;
+	uint16_t sender_priol, sender_priou;
+	char sender_addr[6], sender_addrmsk[6];
+	uint16_t portl, portu;
+	uint16_t msg_agel, msg_ageu;
+	uint16_t max_agel, max_ageu;
+	uint16_t hello_timel, hello_timeu;
+	uint16_t forward_delayl, forward_delayu;
+};
+
+struct ebt_stp_info
+{
+	uint8_t type;
+	struct ebt_stp_config_info config;
+	uint16_t bitmask;
+	uint16_t invflags;
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h b/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h
new file mode 100644
index 0000000..cb1fcc4
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h
@@ -0,0 +1,20 @@
+#ifndef __LINUX_BRIDGE_EBT_VLAN_H
+#define __LINUX_BRIDGE_EBT_VLAN_H
+
+#define EBT_VLAN_ID	0x01
+#define EBT_VLAN_PRIO	0x02
+#define EBT_VLAN_ENCAP	0x04
+#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+#define EBT_VLAN_MATCH "vlan"
+
+struct ebt_vlan_info {
+	uint16_t id;		/* VLAN ID {1-4095} */
+	uint8_t prio;		/* VLAN User Priority {0-7} */
+	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
+	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
+				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+				   bit 2=1 - inversed Pirority arg */
+};
+
+#endif
diff --git a/kernel/linux/include/linux/netfilter_bridge/ebtables.h b/kernel/linux/include/linux/netfilter_bridge/ebtables.h
new file mode 100644
index 0000000..81543a8
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,361 @@
+/*
+ *  ebtables
+ *
+ *	Authors:
+ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
+ *
+ *  ebtables.c,v 2.0, September, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+// verdicts >0 are "branches"
+#define EBT_ACCEPT   -1
+#define EBT_DROP     -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN   -4
+#define NUM_STANDARD_TARGETS   4
+
+struct ebt_counter
+{
+	uint64_t pcnt;
+	uint64_t bcnt;
+};
+
+struct ebt_entries {
+	// this field is always set to zero
+	// See EBT_ENTRY_OR_ENTRIES.
+	// Must be same size as ebt_entry.bitmask
+	unsigned int distinguisher;
+	// the chain name
+	char name[EBT_CHAIN_MAXNAMELEN];
+	// counter offset for this chain
+	unsigned int counter_offset;
+	// one standard (accept, drop, return) per hook
+	int policy;
+	// nr. of entries
+	unsigned int nentries;
+	// entry list
+	char data[0];
+};
+
+// used for the bitmask of struct ebt_entry
+
+// This is a hack to make a difference between an ebt_entry struct and an
+// ebt_entries struct when traversing the entries from start to end.
+// Using this simplifies the code alot, while still being able to use
+// ebt_entries.
+// Contrary, iptables doesn't use something like ebt_entries and therefore uses
+// different techniques for naming the policy and such. So, iptables doesn't
+// need a hack like this.
+#define EBT_ENTRY_OR_ENTRIES 0x01
+// these are the normal masks
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+   | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_match *match;
+	} u;
+	// size of data
+	unsigned int match_size;
+	unsigned char data[0];
+};
+
+struct ebt_entry_watcher
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_watcher *watcher;
+	} u;
+	// size of data
+	unsigned int watcher_size;
+	unsigned char data[0];
+};
+
+struct ebt_entry_target
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_target *target;
+	} u;
+	// size of data
+	unsigned int target_size;
+	unsigned char data[0];
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+	struct ebt_entry_target target;
+	int verdict;
+};
+
+// one entry
+struct ebt_entry {
+	// this needs to be the first field
+	unsigned int bitmask;
+	unsigned int invflags;
+	uint16_t ethproto;
+	// the physical in-dev
+	char in[IFNAMSIZ];
+	// the logical in-dev
+	char logical_in[IFNAMSIZ];
+	// the physical out-dev
+	char out[IFNAMSIZ];
+	// the logical out-dev
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	// sizeof ebt_entry + matches
+	unsigned int watchers_offset;
+	// sizeof ebt_entry + matches + watchers
+	unsigned int target_offset;
+	// sizeof ebt_entry + matches + watchers + target
+	unsigned int next_offset;
+	unsigned char elems[0];
+};
+
+struct ebt_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	// nr of rules in the table
+	unsigned int nentries;
+	// total size of the entries
+	unsigned int entries_size;
+	// start of the chains
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	// nr of counters userspace expects back
+	unsigned int num_counters;
+	// where the kernel will put the old counters
+	struct ebt_counter *counters;
+	char *entries;
+};
+
+// [gs]etsockopt numbers
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+#ifdef __KERNEL__
+
+// return values for match() functions
+#define EBT_MATCH 0
+#define EBT_NOMATCH 1
+
+struct ebt_match
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	// 0 == it matches
+	int (*match)(const struct sk_buff *skb, const struct net_device *in,
+	   const struct net_device *out, const void *matchdata,
+	   unsigned int datalen);
+	// 0 == let it in
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+	void (*destroy)(void *matchdata, unsigned int datalen);
+	struct module *me;
+};
+
+struct ebt_watcher
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
+	   const struct net_device *out, const void *watcherdata,
+	   unsigned int datalen);
+	// 0 == let it in
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+	void (*destroy)(void *watcherdata, unsigned int datalen);
+	struct module *me;
+};
+
+struct ebt_target
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	// returns one of the standard verdicts
+	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
+	   const struct net_device *in, const struct net_device *out,
+	   const void *targetdata, unsigned int datalen);
+	// 0 == let it in
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+	void (*destroy)(void *targetdata, unsigned int datalen);
+	struct module *me;
+};
+
+// used for jumping from and into user defined chains (udc)
+struct ebt_chainstack
+{
+	struct ebt_entries *chaininfo; // pointer to chain data
+	struct ebt_entry *e; // pointer to entry data
+	unsigned int n; // n'th entry
+};
+
+struct ebt_table_info
+{
+	// total size of the entries
+	unsigned int entries_size;
+	unsigned int nentries;
+	// pointers to the start of the chains
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	// room to maintain the stack used for jumping from and into udc
+	struct ebt_chainstack **chainstack;
+	char *entries;
+	struct ebt_counter counters[0] ____cacheline_aligned;
+};
+
+struct ebt_table
+{
+	struct list_head list;
+	char name[EBT_TABLE_MAXNAMELEN];
+	struct ebt_replace *table;
+	unsigned int valid_hooks;
+	rwlock_t lock;
+	// e.g. could be the table explicitly only allows certain
+	// matches, targets, ... 0 == let it in
+	int (*check)(const struct ebt_table_info *info,
+	   unsigned int valid_hooks);
+	// the data used by the kernel
+	struct ebt_table_info *private;
+};
+
+#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
+		     ~(__alignof__(struct ebt_entry_target)-1))
+extern int ebt_register_table(struct ebt_table *table);
+extern void ebt_unregister_table(struct ebt_table *table);
+extern int ebt_register_match(struct ebt_match *match);
+extern void ebt_unregister_match(struct ebt_match *match);
+extern int ebt_register_watcher(struct ebt_watcher *watcher);
+extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
+extern int ebt_register_target(struct ebt_target *target);
+extern void ebt_unregister_target(struct ebt_target *target);
+extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   struct ebt_table *table);
+
+   // Used in the kernel match() functions
+#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
+// True if the hook mask denotes that the rule is in a base chain,
+// used in the check() functions
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+// Clear the bit in the hook mask that tells if the rule is on a base chain
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+// True if the target is not a standard target
+#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
+
+#endif /* __KERNEL__ */
+
+// blatently stolen from ip_tables.h
+// fn returns 0 to continue iteration
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_match *__match;                    \
+	                                                    \
+	for (__i = sizeof(struct ebt_entry);                \
+	     __i < (e)->watchers_offset;                    \
+	     __i += __match->match_size +                   \
+	     sizeof(struct ebt_entry_match)) {              \
+		__match = (void *)(e) + __i;                \
+		                                            \
+		__ret = fn(__match , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->watchers_offset)            \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_watcher *__watcher;                \
+	                                                    \
+	for (__i = e->watchers_offset;                      \
+	     __i < (e)->target_offset;                      \
+	     __i += __watcher->watcher_size +               \
+	     sizeof(struct ebt_entry_watcher)) {            \
+		__watcher = (void *)(e) + __i;              \
+		                                            \
+		__ret = fn(__watcher , ## args);            \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->target_offset)              \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry *__entry;                          \
+	                                                    \
+	for (__i = 0; __i < (size);) {                      \
+		__entry = (void *)(entries) + __i;          \
+		__ret = fn(__entry , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+		if (__entry->bitmask != 0)                  \
+			__i += __entry->next_offset;        \
+		else                                        \
+			__i += sizeof(struct ebt_entries);  \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (size))                          \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#endif
diff --git a/kernel/linux/net/Config.in b/kernel/linux/net/Config.in
new file mode 100644
index 0000000..177e90c
--- /dev/null
+++ b/kernel/linux/net/Config.in
@@ -0,0 +1,105 @@
+#
+# Network configuration
+#
+mainmenu_option next_comment
+comment 'Networking options'
+tristate 'Packet socket' CONFIG_PACKET
+if [ "$CONFIG_PACKET" != "n" ]; then
+   bool '  Packet socket: mmapped IO' CONFIG_PACKET_MMAP
+fi
+
+tristate 'Netlink device emulation' CONFIG_NETLINK_DEV
+
+bool 'Network packet filtering (replaces ipchains)' CONFIG_NETFILTER
+if [ "$CONFIG_NETFILTER" = "y" ]; then
+   bool '  Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
+fi
+bool 'Socket Filtering'  CONFIG_FILTER
+tristate 'Unix domain sockets' CONFIG_UNIX
+bool 'TCP/IP networking' CONFIG_INET
+if [ "$CONFIG_INET" = "y" ]; then
+   source net/ipv4/Config.in
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+#   IPv6 as module will cause a CRASH if you try to unload it
+      tristate '  The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+      if [ "$CONFIG_IPV6" != "n" ]; then
+	 source net/ipv6/Config.in
+      fi
+   fi
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      source net/khttpd/Config.in
+   fi
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
+   if [ "$CONFIG_ATM" = "y" ]; then
+      if [ "$CONFIG_INET" = "y" ]; then
+	 bool '  Classical IP over ATM' CONFIG_ATM_CLIP
+	 if [ "$CONFIG_ATM_CLIP" = "y" ]; then
+	    bool '    Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP
+	 fi
+      fi
+      tristate '  LAN Emulation (LANE) support' CONFIG_ATM_LANE
+      if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then
+	 tristate '    Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA
+      fi
+      tristate '  RFC1483/2684 Bridged protocols' CONFIG_ATM_BR2684
+      if [ "$CONFIG_ATM_BR2684" != "n" ]; then
+            bool '    Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER
+      fi
+   fi
+fi
+tristate '802.1Q VLAN Support' CONFIG_VLAN_8021Q
+
+comment ' '
+tristate 'The IPX protocol' CONFIG_IPX
+if [ "$CONFIG_IPX" != "n" ]; then
+   source net/ipx/Config.in
+fi
+
+tristate 'Appletalk protocol support' CONFIG_ATALK
+source drivers/net/appletalk/Config.in
+
+tristate 'DECnet Support' CONFIG_DECNET
+if [ "$CONFIG_DECNET" != "n" ]; then
+   source net/decnet/Config.in
+fi
+dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
+if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+   source net/bridge/netfilter/Config.in
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+   tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+   bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+   bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
+#   if [ "$CONFIG_LLC" = "y" ]; then
+#      bool '  Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+#   fi
+   if [ "$CONFIG_INET" = "y" ]; then
+      tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
+      if [ "$CONFIG_ECONET" != "n" ]; then
+	 bool '  AUN over UDP' CONFIG_ECONET_AUNUDP
+	 bool '  Native Econet' CONFIG_ECONET_NATIVE
+      fi
+   fi
+   tristate 'WAN router' CONFIG_WAN_ROUTER
+   bool 'Fast switching (read help!)' CONFIG_NET_FASTROUTE
+   bool 'Forwarding between high speed interfaces' CONFIG_NET_HW_FLOWCONTROL
+fi
+
+mainmenu_option next_comment
+comment 'QoS and/or fair queueing'
+bool 'QoS and/or fair queueing' CONFIG_NET_SCHED
+if [ "$CONFIG_NET_SCHED" = "y" ]; then
+   source net/sched/Config.in
+fi
+#bool 'Network code profiler' CONFIG_NET_PROFILE
+endmenu
+
+mainmenu_option next_comment
+comment 'Network testing'
+tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN
+endmenu
+
+endmenu
diff --git a/kernel/linux/net/Makefile b/kernel/linux/net/Makefile
new file mode 100644
index 0000000..0e9b28f
--- /dev/null
+++ b/kernel/linux/net/Makefile
@@ -0,0 +1,63 @@
+#
+# Makefile for the linux networking.
+#
+# 2 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+O_TARGET :=	network.o
+
+mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
+	bluetooth atm netlink sched core
+export-objs :=	netsyms.o
+
+subdir-y :=	core ethernet
+subdir-m :=	ipv4 # hum?
+
+
+subdir-$(CONFIG_NET)		+= 802 sched netlink
+subdir-$(CONFIG_INET)		+= ipv4
+subdir-$(CONFIG_NETFILTER)	+= ipv4/netfilter
+subdir-$(CONFIG_UNIX)		+= unix
+subdir-$(CONFIG_IPV6)		+= ipv6
+
+ifneq ($(CONFIG_IPV6),n)
+ifneq ($(CONFIG_IPV6),)
+subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
+endif
+endif
+
+ifneq ($(CONFIG_BRIDGE),n)
+ifneq ($(CONFIG_BRIDGE),)
+subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+endif
+endif
+
+subdir-$(CONFIG_KHTTPD)		+= khttpd
+subdir-$(CONFIG_PACKET)		+= packet
+subdir-$(CONFIG_NET_SCHED)	+= sched
+subdir-$(CONFIG_BRIDGE)		+= bridge
+subdir-$(CONFIG_IPX)		+= ipx
+subdir-$(CONFIG_ATALK)		+= appletalk
+subdir-$(CONFIG_WAN_ROUTER)	+= wanrouter
+subdir-$(CONFIG_X25)		+= x25
+subdir-$(CONFIG_LAPB)		+= lapb
+subdir-$(CONFIG_NETROM)		+= netrom
+subdir-$(CONFIG_ROSE)		+= rose
+subdir-$(CONFIG_AX25)		+= ax25
+subdir-$(CONFIG_IRDA)		+= irda
+subdir-$(CONFIG_BLUEZ)		+= bluetooth
+subdir-$(CONFIG_SUNRPC)		+= sunrpc
+subdir-$(CONFIG_ATM)		+= atm
+subdir-$(CONFIG_DECNET)		+= decnet
+subdir-$(CONFIG_ECONET)		+= econet
+subdir-$(CONFIG_VLAN_8021Q)           += 8021q
+
+
+obj-y	:= socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y))))
+ifeq ($(CONFIG_NET),y)
+obj-$(CONFIG_MODULES)		+= netsyms.o
+obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/kernel/linux/net/bridge/Makefile b/kernel/linux/net/bridge/Makefile
new file mode 100644
index 0000000..37e4464
--- /dev/null
+++ b/kernel/linux/net/bridge/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the IEEE 802.1d ethernet bridging layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+export-objs := br.o
+
+O_TARGET	:= bridge.o
+obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+			br_stp_if.o br_stp_timer.o
+obj-m		:= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/kernel/linux/net/bridge/br.c b/kernel/linux/net/bridge/br.c
new file mode 100644
index 0000000..1c7b938
--- /dev/null
+++ b/kernel/linux/net/bridge/br.c
@@ -0,0 +1,83 @@
+/*
+ *	Generic parts
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br.c,v 1.4 2002/09/16 21:10:43 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/if_bridge.h>
+#include <asm/uaccess.h>
+#include "br_private.h"
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "../atm/lec.h"
+#endif
+
+int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+
+void br_dec_use_count()
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void br_inc_use_count()
+{
+	MOD_INC_USE_COUNT;
+}
+
+static int __init br_init(void)
+{
+	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+
+	br_handle_frame_hook = br_handle_frame;
+	br_ioctl_hook = br_ioctl_deviceless_stub;
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = br_fdb_get;
+	br_fdb_put_hook = br_fdb_put;
+#endif
+	register_netdevice_notifier(&br_device_notifier);
+
+	return 0;
+}
+
+static void __br_clear_frame_hook(void)
+{
+	br_handle_frame_hook = NULL;
+}
+
+static void __br_clear_ioctl_hook(void)
+{
+	br_ioctl_hook = NULL;
+}
+
+static void __exit br_deinit(void)
+{
+	unregister_netdevice_notifier(&br_device_notifier);
+	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+	net_call_rx_atomic(__br_clear_frame_hook);
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = NULL;
+	br_fdb_put_hook = NULL;
+#endif
+}
+
+EXPORT_SYMBOL(br_should_route_hook);
+
+module_init(br_init)
+module_exit(br_deinit)
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/br_forward.c b/kernel/linux/net/bridge/br_forward.c
new file mode 100644
index 0000000..6ffd5a6
--- /dev/null
+++ b/kernel/linux/net/bridge/br_forward.c
@@ -0,0 +1,151 @@
+/*
+ *	Forwarding decision
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_forward.c,v 1.1 2002/09/10 17:40:54 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
+{
+	if (skb->dev == p->dev ||
+	    p->state != BR_STATE_FORWARDING)
+		return 0;
+
+	return 1;
+}
+
+static int __dev_queue_push_xmit(struct sk_buff *skb)
+{
+	skb_push(skb, ETH_HLEN);
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+static int __br_forward_finish(struct sk_buff *skb)
+{
+	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+			__dev_queue_push_xmit);
+
+	return 0;
+}
+
+static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	skb->dev = to->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			__br_forward_finish);
+}
+
+static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	indev = skb->dev;
+	skb->dev = to->dev;
+
+	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+			__br_forward_finish);
+}
+
+/* called under bridge lock */
+void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_deliver(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_forward(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
+	void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
+{
+	struct net_bridge_port *p;
+	struct net_bridge_port *prev;
+
+	if (clone) {
+		struct sk_buff *skb2;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			br->statistics.tx_dropped++;
+			return;
+		}
+
+		skb = skb2;
+	}
+
+	prev = NULL;
+
+	p = br->port_list;
+	while (p != NULL) {
+		if (should_deliver(p, skb)) {
+			if (prev != NULL) {
+				struct sk_buff *skb2;
+
+				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+					br->statistics.tx_dropped++;
+					kfree_skb(skb);
+					return;
+				}
+
+				__packet_hook(prev, skb2);
+			}
+
+			prev = p;
+		}
+
+		p = p->next;
+	}
+
+	if (prev != NULL) {
+		__packet_hook(prev, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_deliver);
+}
+
+/* called under bridge lock */
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_forward);
+}
diff --git a/kernel/linux/net/bridge/br_input.c b/kernel/linux/net/bridge/br_input.c
new file mode 100644
index 0000000..aed60eb
--- /dev/null
+++ b/kernel/linux/net/bridge/br_input.c
@@ -0,0 +1,179 @@
+/*
+ *	Handle incoming frames
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_input.c,v 1.7 2002/09/29 18:27:32 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+static int br_pass_frame_up_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	netif_rx(skb);
+
+	return 0;
+}
+
+static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	br->statistics.rx_packets++;
+	br->statistics.rx_bytes += skb->len;
+
+	indev = skb->dev;
+	skb->dev = &br->dev;
+	skb->pkt_type = PACKET_HOST;
+	skb_push(skb, ETH_HLEN);
+	skb->protocol = eth_type_trans(skb, &br->dev);
+
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
+			br_pass_frame_up_finish);
+}
+
+static int br_handle_frame_finish(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_fdb_entry *dst;
+	struct net_bridge_port *p;
+	int passedup;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	passedup = 0;
+	if (br->dev.flags & IFF_PROMISC) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 != NULL) {
+			passedup = 1;
+			br_pass_frame_up(br, skb2);
+		}
+	}
+
+	if (dest[0] & 1) {
+		br_flood_forward(br, skb, !passedup);
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		goto out;
+	}
+
+	dst = br_fdb_get(br, dest);
+	if (dst != NULL && dst->is_local) {
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		else
+			kfree_skb(skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	if (dst != NULL) {
+		br_forward(dst->dst, skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	br_flood_forward(br, skb, 0);
+
+out:
+	read_unlock(&br->lock);
+	return 0;
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+}
+
+int br_handle_frame(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_port *p;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	if (!(br->dev.flags & IFF_UP) ||
+	    p->state == BR_STATE_DISABLED)
+		goto err;
+
+	if (skb->mac.ethernet->h_source[0] & 1)
+		goto err;
+
+	if (p->state == BR_STATE_LEARNING ||
+	    p->state == BR_STATE_FORWARDING)
+		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+	if (br->stp_enabled &&
+	    !memcmp(dest, bridge_ula, 5) &&
+	    !(dest[5] & 0xF0))
+		goto handle_special_frame;
+
+	if (p->state == BR_STATE_FORWARDING) {
+		if (br_should_route_hook && br_should_route_hook(&skb)) {
+			read_unlock(&br->lock);
+			return -1;
+		}
+
+		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+			br_handle_frame_finish);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+
+handle_special_frame:
+	if (!dest[5]) {
+		br_stp_handle_bpdu(skb);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+	read_unlock(&br->lock);
+	kfree_skb(skb);
+	return 0;
+}
diff --git a/kernel/linux/net/bridge/br_private.h b/kernel/linux/net/bridge/br_private.h
new file mode 100644
index 0000000..df41d0f
--- /dev/null
+++ b/kernel/linux/net/bridge/br_private.h
@@ -0,0 +1,204 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_private.h,v 1.3 2002/08/29 21:25:05 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _BR_PRIVATE_H
+#define _BR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/if_bridge.h>
+#include "br_private_timer.h"
+
+#define BR_HASH_BITS 8
+#define BR_HASH_SIZE (1 << BR_HASH_BITS)
+
+#define BR_HOLD_TIME (1*HZ)
+
+typedef struct bridge_id bridge_id;
+typedef struct mac_addr mac_addr;
+typedef __u16 port_id;
+
+struct bridge_id
+{
+	unsigned char	prio[2];
+	unsigned char	addr[6];
+};
+
+struct mac_addr
+{
+	unsigned char	addr[6];
+	unsigned char	pad[2];
+};
+
+struct net_bridge_fdb_entry
+{
+	struct net_bridge_fdb_entry	*next_hash;
+	struct net_bridge_fdb_entry	**pprev_hash;
+	atomic_t			use_count;
+	mac_addr			addr;
+	struct net_bridge_port		*dst;
+	unsigned long			ageing_timer;
+	unsigned			is_local:1;
+	unsigned			is_static:1;
+};
+
+struct net_bridge_port
+{
+	struct net_bridge_port		*next;
+	struct net_bridge		*br;
+	struct net_device		*dev;
+	int				port_no;
+
+	/* STP */
+	port_id				port_id;
+	int				state;
+	int				path_cost;
+	bridge_id			designated_root;
+	int				designated_cost;
+	bridge_id			designated_bridge;
+	port_id				designated_port;
+	unsigned			topology_change_ack:1;
+	unsigned			config_pending:1;
+	int				priority;
+
+	struct br_timer			forward_delay_timer;
+	struct br_timer			hold_timer;
+	struct br_timer			message_age_timer;
+};
+
+struct net_bridge
+{
+	struct net_bridge		*next;
+	rwlock_t			lock;
+	struct net_bridge_port		*port_list;
+	struct net_device		dev;
+	struct net_device_stats		statistics;
+	rwlock_t			hash_lock;
+	struct net_bridge_fdb_entry	*hash[BR_HASH_SIZE];
+	struct timer_list		tick;
+
+	/* STP */
+	bridge_id			designated_root;
+	int				root_path_cost;
+	int				root_port;
+	int				max_age;
+	int				hello_time;
+	int				forward_delay;
+	bridge_id			bridge_id;
+	int				bridge_max_age;
+	int				bridge_hello_time;
+	int				bridge_forward_delay;
+	unsigned			stp_enabled:1;
+	unsigned			topology_change:1;
+	unsigned			topology_change_detected:1;
+
+	struct br_timer			hello_timer;
+	struct br_timer			tcn_timer;
+	struct br_timer			topology_change_timer;
+	struct br_timer			gc_timer;
+
+	int				ageing_time;
+	int				gc_interval;
+};
+
+extern struct notifier_block br_device_notifier;
+extern unsigned char bridge_ula[6];
+
+/* br.c */
+extern void br_dec_use_count(void);
+extern void br_inc_use_count(void);
+
+/* br_device.c */
+extern void br_dev_setup(struct net_device *dev);
+extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* br_fdb.c */
+extern void br_fdb_changeaddr(struct net_bridge_port *p,
+		       unsigned char *newaddr);
+extern void br_fdb_cleanup(struct net_bridge *br);
+extern void br_fdb_delete_by_port(struct net_bridge *br,
+			   struct net_bridge_port *p);
+extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
+					unsigned char *addr);
+extern void br_fdb_put(struct net_bridge_fdb_entry *ent);
+extern int  br_fdb_get_entries(struct net_bridge *br,
+			unsigned char *_buf,
+			int maxnum,
+			int offset);
+extern void br_fdb_insert(struct net_bridge *br,
+		   struct net_bridge_port *source,
+		   unsigned char *addr,
+		   int is_local);
+
+/* br_forward.c */
+extern void br_deliver(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern void br_forward(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern void br_flood_deliver(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+extern void br_flood_forward(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+
+/* br_if.c */
+extern int br_add_bridge(char *name);
+extern int br_del_bridge(char *name);
+extern int br_add_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_del_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_get_bridge_ifindices(int *indices,
+			    int num);
+extern void br_get_port_ifindices(struct net_bridge *br,
+			   int *ifindices);
+
+/* br_input.c */
+extern int br_handle_frame(struct sk_buff *skb);
+
+/* br_ioctl.c */
+extern void br_call_ioctl_atomic(void (*fn)(void));
+extern int br_ioctl(struct net_bridge *br,
+	     unsigned int cmd,
+	     unsigned long arg0,
+	     unsigned long arg1,
+	     unsigned long arg2);
+extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_stp.c */
+extern int br_is_root_bridge(struct net_bridge *br);
+extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+				    int port_no);
+extern void br_init_port(struct net_bridge_port *p);
+extern port_id br_make_port_id(struct net_bridge_port *p);
+extern void br_become_designated_port(struct net_bridge_port *p);
+
+/* br_stp_if.c */
+extern void br_stp_enable_bridge(struct net_bridge *br);
+extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_enable_port(struct net_bridge_port *p);
+extern void br_stp_disable_port(struct net_bridge_port *p);
+extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
+extern void br_stp_set_bridge_priority(struct net_bridge *br,
+				int newprio);
+extern void br_stp_set_port_priority(struct net_bridge_port *p,
+			      int newprio);
+extern void br_stp_set_path_cost(struct net_bridge_port *p,
+			  int path_cost);
+
+/* br_stp_bpdu.c */
+extern void br_stp_handle_bpdu(struct sk_buff *skb);
+
+#endif
diff --git a/kernel/linux/net/bridge/netfilter/Config.in b/kernel/linux/net/bridge/netfilter/Config.in
new file mode 100644
index 0000000..07cb3af
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/Config.in
@@ -0,0 +1,22 @@
+#
+# Bridge netfilter configuration
+#
+dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
+dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: among filter support' CONFIG_BRIDGE_EBT_AMONG $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: limit filter support' CONFIG_BRIDGE_EBT_LIMIT $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
+dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
diff --git a/kernel/linux/net/bridge/netfilter/Makefile b/kernel/linux/net/bridge/netfilter/Makefile
new file mode 100644
index 0000000..ef67365
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for the netfilter modules on top of bridging.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET	:= netfilter.o
+
+export-objs := ebtables.o
+
+obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
+obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
+obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
+obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
+obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
+obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
+obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
+obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
+obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
+include $(TOPDIR)/Rules.make
diff --git a/kernel/linux/net/bridge/netfilter/ebt_802_3.c b/kernel/linux/net/bridge/netfilter/ebt_802_3.c
new file mode 100644
index 0000000..4b48398
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_802_3.c
@@ -0,0 +1,74 @@
+/*
+ * 802_3
+ *
+ * Author:
+ * Chris Vitale csv@bluetail.com
+ *
+ * May 2003
+ * 
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_802_3.h>
+#include <linux/module.h>
+
+static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
+	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
+
+	if (info->bitmask & EBT_802_3_SAP) {
+		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
+				return EBT_NOMATCH;
+		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
+				return EBT_NOMATCH;
+	}
+
+	if (info->bitmask & EBT_802_3_TYPE) {
+		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
+			return EBT_NOMATCH;
+		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
+			return EBT_NOMATCH;
+	}
+
+	return EBT_MATCH;
+}
+
+static struct ebt_match filter_802_3;
+static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct ebt_match filter_802_3 =
+{
+	.name		= EBT_802_3_MATCH,
+	.match		= ebt_filter_802_3,
+	.check		= ebt_802_3_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_802_3);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_802_3);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_among.c b/kernel/linux/net/bridge/netfilter/ebt_among.c
new file mode 100644
index 0000000..3fb93dd
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_among.c
@@ -0,0 +1,223 @@
+/*
+ *  ebt_among
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ *  August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+
+static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
+				     const char *mac, uint32_t ip)
+{
+	/* You may be puzzled as to how this code works.
+	 * Some tricks were used, refer to 
+	 * 	include/linux/netfilter_bridge/ebt_among.h
+	 * as there you can find a solution of this mystery.
+	 */
+	const struct ebt_mac_wormhash_tuple *p;
+	int start, limit, i;
+	uint32_t cmp[2] = { 0, 0 };
+	int key = (const unsigned char) mac[5];
+
+	memcpy(((char *) cmp) + 2, mac, 6);
+	start = wh->table[key];
+	limit = wh->table[key + 1];
+	if (ip) {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0 || p->ip == ip) {
+					return 1;
+				}
+			}
+		}
+	} else {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0) {
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
+					    *wh)
+{
+	int i;
+
+	for (i = 0; i < 256; i++) {
+		if (wh->table[i] > wh->table[i + 1])
+			return -0x100 - i;
+		if (wh->table[i] < 0)
+			return -0x200 - i;
+		if (wh->table[i] > wh->poolsize)
+			return -0x300 - i;
+	}
+	if (wh->table[256] > wh->poolsize)
+		return -0xc00;
+	return 0;
+}
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t * addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
+		*addr = skb->nh.iph->daddr;
+	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		uint32_t arp_len = sizeof(struct arphdr) +
+		    (2 * (((*skb).nh.arph)->ar_hln)) +
+		    (2 * (((*skb).nh.arph)->ar_pln));
+
+		/* Make sure the packet is long enough. */
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return -1;
+		/* IPv4 addresses are always 4 bytes. */
+		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+			return -1;
+
+		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+		       (2 * (((*skb).nh.arph)->ar_hln)) +
+		       (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
+
+	}
+	return 0;
+}
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t * addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
+		*addr = skb->nh.iph->saddr;
+	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		uint32_t arp_len = sizeof(struct arphdr) +
+		    (2 * (((*skb).nh.arph)->ar_hln)) +
+		    (2 * (((*skb).nh.arph)->ar_pln));
+
+		/* Make sure the packet is long enough. */
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return -1;
+		/* IPv4 addresses are always 4 bytes. */
+		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+			return -1;
+
+		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+		       ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
+
+	}
+	return 0;
+}
+
+static int ebt_filter_among(const struct sk_buff *skb,
+			    const struct net_device *in,
+			    const struct net_device *out, const void *data,
+			    unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	const char *dmac, *smac;
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	uint32_t dip = 0, sip = 0;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+
+	if (wh_src) {
+		smac = skb->mac.ethernet->h_source;
+		if (get_ip_src(skb, &sip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	if (wh_dst) {
+		dmac = skb->mac.ethernet->h_dest;
+		if (get_ip_dst(skb, &dip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	return EBT_MATCH;
+}
+
+static int ebt_among_check(const char *tablename, unsigned int hookmask,
+			   const struct ebt_entry *e, void *data,
+			   unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	int expected_length = sizeof(struct ebt_among_info);
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	int err;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+	expected_length += ebt_mac_wormhash_size(wh_dst);
+	expected_length += ebt_mac_wormhash_size(wh_src);
+
+	if (datalen != EBT_ALIGN(expected_length)) {
+		printk(KERN_WARNING
+		       "ebtables: among: wrong size: %d"
+		       "against expected %d, rounded to %d\n",
+		       datalen, expected_length,
+		       EBT_ALIGN(expected_length));
+		return -EINVAL;
+	}
+	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
+		printk(KERN_WARNING
+		       "ebtables: among: dst integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
+		printk(KERN_WARNING
+		       "ebtables: among: src integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct ebt_match filter_among = {
+	{NULL, NULL}, 
+	EBT_AMONG_MATCH, 
+	ebt_filter_among, 
+	ebt_among_check,
+	NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_among);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_among);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_arp.c b/kernel/linux/net/bridge/netfilter/ebt_arp.c
new file mode 100644
index 0000000..4bb1281
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_arp.c
@@ -0,0 +1,149 @@
+/*
+ *  ebt_arp
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *	Tim Gardner <timg@tpi.com>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/module.h>
+
+static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
+		return EBT_NOMATCH;
+
+	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
+	{
+		uint32_t arp_len = sizeof(struct arphdr) +
+		   (2 * (((*skb).nh.arph)->ar_hln)) +
+		   (2 * (((*skb).nh.arph)->ar_pln));
+		uint32_t dst;
+		uint32_t src;
+
+		// Make sure the packet is long enough.
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return EBT_NOMATCH;
+		// IPv4 addresses are always 4 bytes.
+		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+			return EBT_NOMATCH;
+
+		if (info->bitmask & EBT_ARP_SRC_IP) {
+			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
+			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
+			if (FWINV(info->saddr != (src & info->smsk),
+			   EBT_ARP_SRC_IP))
+				return EBT_NOMATCH;
+		}
+
+		if (info->bitmask & EBT_ARP_DST_IP) {
+			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
+			   (2*(((*skb).nh.arph)->ar_hln)) +
+			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
+			if (FWINV(info->daddr != (dst & info->dmsk),
+			   EBT_ARP_DST_IP))
+				return EBT_NOMATCH;
+		}
+	}
+
+	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
+	{
+		uint32_t arp_len = sizeof(struct arphdr) +
+		   (2 * (((*skb).nh.arph)->ar_hln)) +
+		   (2 * (((*skb).nh.arph)->ar_pln));
+		unsigned char dst[ETH_ALEN];
+		unsigned char src[ETH_ALEN];
+
+		// Make sure the packet is long enough.
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return EBT_NOMATCH;
+		// MAC addresses are 6 bytes.
+		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_SRC_MAC) {
+			uint8_t verdict, i;
+
+			memcpy(&src, ((*skb).nh.raw) +
+					sizeof(struct arphdr),
+					ETH_ALEN);
+			verdict = 0;
+			for (i = 0; i < 6; i++)
+				verdict |= (src[i] ^ info->smaddr[i]) &
+				       info->smmsk[i];	
+			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
+				return EBT_NOMATCH;
+		}
+
+		if (info->bitmask & EBT_ARP_DST_MAC) { 
+			uint8_t verdict, i;
+
+			memcpy(&dst, ((*skb).nh.raw) +
+					sizeof(struct arphdr) +
+			   		(((*skb).nh.arph)->ar_hln) +
+			   		(((*skb).nh.arph)->ar_pln),
+					ETH_ALEN);
+			verdict = 0;
+			for (i = 0; i < 6; i++)
+				verdict |= (dst[i] ^ info->dmaddr[i]) &
+					info->dmmsk[i];
+			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
+				return EBT_NOMATCH;
+		}
+	}
+
+	return EBT_MATCH;
+}
+
+static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
+		return -EINVAL;
+	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
+	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
+	   e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_arp =
+{
+	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_arp);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_arp);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_arpreply.c b/kernel/linux/net/bridge/netfilter/ebt_arpreply.c
new file mode 100644
index 0000000..01ec338
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_arpreply.c
@@ -0,0 +1,86 @@
+/*
+ *  ebt_arpreply
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arpreply.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/module.h>
+
+static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
+	struct arphdr *ah;
+	unsigned char *sha, *arp_ptr;
+	u32 sip, tip;
+
+	ah = (**pskb).nh.arph;
+	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
+	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
+	    ah->ar_pln != 4)
+		return EBT_CONTINUE;
+
+	arp_ptr = (unsigned char *)(ah + 1);
+
+	/* get source and target IP */
+	sha = arp_ptr;
+	arp_ptr += ETH_ALEN;
+	memcpy(&sip, arp_ptr, 4);
+	arp_ptr += 4 + ETH_ALEN;
+	memcpy(&tip, arp_ptr, 4);
+
+	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
+
+	return info->target;
+}
+
+static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
+	    e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target reply_target =
+{
+	.name		= EBT_ARPREPLY_TARGET,
+	.target		= ebt_target_reply,
+	.check		= ebt_target_reply_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&reply_target);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&reply_target);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_dnat.c b/kernel/linux/net/bridge/netfilter/ebt_dnat.c
new file mode 100644
index 0000000..d3d1b4f
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_dnat.c
@@ -0,0 +1,65 @@
+/*
+ *  ebt_dnat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
+	   ETH_ALEN * sizeof(unsigned char));
+	return info->target;
+}
+
+static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if ( (strcmp(tablename, "nat") ||
+	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+		return -EINVAL;
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target dnat =
+{
+	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
+	NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&dnat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&dnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_idnat.c b/kernel/linux/net/bridge/netfilter/ebt_idnat.c
new file mode 100644
index 0000000..75ea05b
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_idnat.c
@@ -0,0 +1,127 @@
+/*
+ *  ebt_idnat
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ *  September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_inat.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		*addr = skb->nh.iph->daddr;
+		return 1;
+	}
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+
+		uint32_t arp_len = sizeof(struct arphdr) +
+		   (2 * (((*skb).nh.arph)->ar_hln)) +
+		   (2 * (((*skb).nh.arph)->ar_pln));
+
+		// Make sure the packet is long enough.
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return 0;
+		// IPv4 addresses are always 4 bytes.
+		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+			return 0;
+
+		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+		   (2*(((*skb).nh.arph)->ar_hln)) +
+		   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
+		
+		return 2;
+	}
+	return 0;
+}
+
+static int ebt_target_idnat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+	uint32_t ip;
+	int index;
+	struct ebt_inat_tuple *tuple;
+	
+	if (!get_ip_dst(*pskb, &ip)) {
+		/* not an ARP or IPV4 packet */
+		return info->target;
+	}
+
+	if ((ip & __constant_htonl(0xffffff00)) != info->ip_subnet) {
+
+		/* outside our range */
+		return info->target;
+	}
+
+	index = ((unsigned char*)&ip)[3]; /* the last byte; network packets are big endian */
+	tuple = &info->a[index];
+
+	if (!tuple->enabled) {
+		/* we do not want to alter packets with this IP */
+		return info->target;
+	}
+	
+	memcpy(((**pskb).mac.ethernet)->h_dest, tuple->mac,
+	   ETH_ALEN * sizeof(unsigned char));
+
+	if ((**pskb).mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		/* change the payload */
+		memcpy(
+		   (**pskb).nh.raw + sizeof(struct arphdr) + 
+		   (**pskb).nh.arph->ar_hln +
+		   (**pskb).nh.arph->ar_pln, tuple->mac, ETH_ALEN);
+	}
+	
+	return tuple->target;
+}
+
+static int ebt_target_idnat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if ( (strcmp(tablename, "nat") ||
+	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+		return -EINVAL;
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_inat_info)))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target idnat =
+{
+	{NULL, NULL}, EBT_IDNAT_TARGET, ebt_target_idnat, ebt_target_idnat_check,
+	NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&idnat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&idnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_ip.c b/kernel/linux/net/bridge/netfilter/ebt_ip.c
new file mode 100644
index 0000000..1631b33
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_ip.c
@@ -0,0 +1,121 @@
+/*
+ *  ebt_ip
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ip.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/module.h>
+
+struct tcpudphdr {
+	uint16_t src;
+	uint16_t dst;
+};
+
+union h_u {
+	unsigned char *raw;
+	struct tcpudphdr *tuh;
+};
+
+static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+	if (info->bitmask & EBT_IP_TOS &&
+	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_IP_PROTO) {
+		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
+		          EBT_IP_PROTO))
+			return EBT_NOMATCH;
+		if ( info->protocol == IPPROTO_TCP ||
+		     info->protocol == IPPROTO_UDP )
+		{
+			union h_u h;
+			h.raw = skb->data + skb->nh.iph->ihl*4;
+			if (info->bitmask & EBT_IP_DPORT) {
+				uint16_t port = ntohs(h.tuh->dst);
+				if (FWINV(port < info->dport[0] ||
+				          port > info->dport[1],
+				          EBT_IP_DPORT))
+				return EBT_NOMATCH;
+			}
+			if (info->bitmask & EBT_IP_SPORT) {
+				uint16_t port = ntohs(h.tuh->src);
+				if (FWINV(port < info->sport[0] ||
+				          port > info->sport[1],
+				          EBT_IP_SPORT))
+				return EBT_NOMATCH;
+			}
+		}
+	}
+	if (info->bitmask & EBT_IP_SOURCE &&
+	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
+	   info->saddr, EBT_IP_SOURCE))
+		return EBT_NOMATCH;
+	if ((info->bitmask & EBT_IP_DEST) &&
+	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
+	   info->daddr, EBT_IP_DEST))
+		return EBT_NOMATCH;
+	return EBT_MATCH;
+}
+
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
+		return -EINVAL;
+	if (e->ethproto != __constant_htons(ETH_P_IP) ||
+	   e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+		return -EINVAL;
+	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
+		if (!info->bitmask & EBT_IPROTO)
+			return -EINVAL;
+		if (info->protocol != IPPROTO_TCP &&
+		    info->protocol != IPPROTO_UDP)
+			 return -EINVAL;
+	}
+	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
+		return -EINVAL;
+	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_ip =
+{
+	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_ip);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_ip);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_isnat.c b/kernel/linux/net/bridge/netfilter/ebt_isnat.c
new file mode 100644
index 0000000..7f2f844
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_isnat.c
@@ -0,0 +1,122 @@
+/*
+ *  ebt_isnat
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ *  September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_inat.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		*addr = skb->nh.iph->saddr;
+		return 1;
+	}
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+
+		uint32_t arp_len = sizeof(struct arphdr) +
+		   (2 * (((*skb).nh.arph)->ar_hln)) +
+		   (2 * (((*skb).nh.arph)->ar_pln));
+
+		// Make sure the packet is long enough.
+		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+			return 0;
+		// IPv4 addresses are always 4 bytes.
+		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+			return 0;
+
+		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
+		   ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
+		
+		return 2;
+	}
+	return 0;
+}
+
+static int ebt_target_isnat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+	uint32_t ip;
+	int index;
+	struct ebt_inat_tuple *tuple;
+	
+	if (!get_ip_src(*pskb, &ip)) {
+		/* not an ARP or IPV4 packet */
+		return info->target;
+	}
+
+	if ((ip & __constant_htonl(0xffffff00)) != info->ip_subnet) {
+
+		/* outside our range */
+		return info->target;
+	}
+
+	index = ((unsigned char*)&ip)[3]; /* the last byte; network packets are big endian */
+	tuple = &info->a[index];
+
+	if (!tuple->enabled) {
+		/* we do not want to alter packets with this IP */
+		return info->target;
+	}
+	
+	memcpy(((**pskb).mac.ethernet)->h_source, tuple->mac, ETH_ALEN);
+
+	if ((**pskb).mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		/* change the payload */
+		memcpy((**pskb).nh.raw + sizeof(struct arphdr), tuple->mac, ETH_ALEN);
+	}
+	
+	return tuple->target;
+}
+
+static int ebt_target_isnat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)data;
+
+	if (datalen != sizeof(struct ebt_inat_info))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (strcmp(tablename, "nat"))
+		return -EINVAL;
+	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target isnat =
+{
+	{NULL, NULL}, EBT_ISNAT_TARGET, ebt_target_isnat, ebt_target_isnat_check,
+	NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&isnat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&isnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_limit.c b/kernel/linux/net/bridge/netfilter/ebt_limit.c
new file mode 100644
index 0000000..f130203
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_limit.c
@@ -0,0 +1,101 @@
+/*
+ *  ebt_limit
+ *
+ *	Authors:
+ *	Tom Marshall <tommy@home.tig-grr.com>
+ *
+ *	Mostly copied from netfilter's ipt_limit.c, see that file for explanation
+ *
+ *  September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_limit.h>
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
+
+#define CREDITS_PER_JIFFY 128
+
+static int ebt_limit_match(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+	unsigned long now = jiffies;
+
+	spin_lock_bh(&limit_lock);
+	info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
+	if (info->credit > info->credit_cap)
+		info->credit = info->credit_cap;
+
+	if (info->credit >= info->cost) {
+		/* We're not limited. */
+		info->credit -= info->cost;
+		spin_unlock_bh(&limit_lock);
+		return EBT_MATCH;
+	}
+
+	spin_unlock_bh(&limit_lock);
+	return EBT_NOMATCH;
+}
+
+/* Precision saver. */
+static u_int32_t
+user2credits(u_int32_t user)
+{
+	/* If multiplying would overflow... */
+	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+		/* Divide first. */
+		return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
+
+	return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
+}
+
+static int ebt_limit_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
+		return -EINVAL;
+
+	/* Check for overflow. */
+	if (info->burst == 0
+	    || user2credits(info->avg * info->burst) < user2credits(info->avg)) {
+		printk("Overflow in ebt_limit: %u/%u\n",
+			info->avg, info->burst);
+		return -EINVAL;
+	}
+
+	/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
+	info->prev = jiffies;
+	info->credit = user2credits(info->avg * info->burst);
+	info->credit_cap = user2credits(info->avg * info->burst);
+	info->cost = user2credits(info->avg);
+	return 0;
+}
+
+static struct ebt_match ebt_limit_reg =
+{
+	{NULL, NULL}, EBT_LIMIT_MATCH, ebt_limit_match, ebt_limit_check, NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&ebt_limit_reg);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&ebt_limit_reg);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_log.c b/kernel/linux/net/bridge/netfilter/ebt_log.c
new file mode 100644
index 0000000..9b8aff1
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_log.c
@@ -0,0 +1,152 @@
+/*
+ *  ebt_log
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_log.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+#include <linux/spinlock.h>
+
+static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
+
+static int ebt_log_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_log_info *info = (struct ebt_log_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_LOG_MASK)
+		return -EINVAL;
+	if (info->loglevel >= 8)
+		return -EINVAL;
+	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+	return 0;
+}
+
+struct tcpudphdr
+{
+	uint16_t src;
+	uint16_t dst;
+};
+
+struct arppayload
+{
+	unsigned char mac_src[ETH_ALEN];
+	unsigned char ip_src[4];
+	unsigned char mac_dst[ETH_ALEN];
+	unsigned char ip_dst[4];
+};
+
+static void print_MAC(unsigned char *p)
+{
+	int i;
+
+	for (i = 0; i < ETH_ALEN; i++, p++)
+		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+}
+
+#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
+static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_log_info *info = (struct ebt_log_info *)data;
+	char level_string[4] = "< >";
+	level_string[1] = '0' + info->loglevel;
+
+	spin_lock_bh(&ebt_log_lock);
+	printk(level_string);
+	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
+	   out ? out->name : "");
+
+	printk("MAC source = ");
+	print_MAC((skb->mac.ethernet)->h_source);
+	printk("MAC dest = ");
+	print_MAC((skb->mac.ethernet)->h_dest);
+
+	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
+
+	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
+	   htons(ETH_P_IP)){
+		struct iphdr *iph = skb->nh.iph;
+		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
+		if (iph->protocol == IPPROTO_TCP ||
+		    iph->protocol == IPPROTO_UDP) {
+			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
+
+			if (skb->data + iph->ihl*4 > skb->tail) {
+				printk(" INCOMPLETE TCP/UDP header");
+				goto out;
+			}
+			printk(" SPT=%u DPT=%u", ntohs(ports->src),
+			   ntohs(ports->dst));
+		}
+		goto out;
+	}
+
+	if ((info->bitmask & EBT_LOG_ARP) &&
+	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
+	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
+		struct arphdr * arph = skb->nh.arph;
+		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
+		   ntohs(arph->ar_op));
+		/* If it's for Ethernet and the lengths are OK,
+		 * then log the ARP payload */
+		if (arph->ar_hrd == __constant_htons(1) &&
+		    arph->ar_hln == ETH_ALEN &&
+		    arph->ar_pln == sizeof(uint32_t)) {
+			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
+
+			if (skb->data + sizeof(*arph) > skb->tail) {
+				printk(" INCOMPLETE ARP header");
+				goto out;
+			}
+
+			printk(" ARP MAC SRC=");
+			print_MAC(arpp->mac_src);
+			printk(" ARP IP SRC=%u.%u.%u.%u",
+			       myNIPQUAD(arpp->ip_src));
+			printk(" ARP MAC DST=");
+			print_MAC(arpp->mac_dst);
+			printk(" ARP IP DST=%u.%u.%u.%u",
+			       myNIPQUAD(arpp->ip_dst));
+		}
+
+	}
+out:
+	printk("\n");
+	spin_unlock_bh(&ebt_log_lock);
+}
+
+static struct ebt_watcher log =
+{
+	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_watcher(&log);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_watcher(&log);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_mark.c b/kernel/linux/net/bridge/netfilter/ebt_mark.c
new file mode 100644
index 0000000..af5c085
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_mark.c
@@ -0,0 +1,66 @@
+/*
+ *  ebt_mark
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  July, 2002
+ *
+ */
+
+// The mark target can be used in any chain
+// I believe adding a mangle table just for marking is total overkill
+// Marking a frame doesn't really change anything in the frame anyway
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+#include <linux/module.h>
+
+static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+	if ((*pskb)->nfmark != info->mark) {
+		(*pskb)->nfmark = info->mark;
+		(*pskb)->nfcache |= NFC_ALTERED;
+	}
+	return info->target;
+}
+
+static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target mark_target =
+{
+	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
+	ebt_target_mark_check, NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&mark_target);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&mark_target);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_mark_m.c b/kernel/linux/net/bridge/netfilter/ebt_mark_m.c
new file mode 100644
index 0000000..66916bb
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_mark_m.c
@@ -0,0 +1,61 @@
+/*
+ *  ebt_mark_m
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  July, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+#include <linux/module.h>
+
+static int ebt_filter_mark(const struct sk_buff *skb,
+   const struct net_device *in, const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+	if (info->bitmask & EBT_MARK_OR)
+		return !(!!(skb->nfmark & info->mask) ^ info->invert);
+	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
+}
+
+static int ebt_mark_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_MARK_MASK)
+		return -EINVAL;
+	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+		return -EINVAL;
+	if (!info->bitmask)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_mark =
+{
+	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
+	THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_mark);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_mark);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_pkttype.c b/kernel/linux/net/bridge/netfilter/ebt_pkttype.c
new file mode 100644
index 0000000..c974dc7
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_pkttype.c
@@ -0,0 +1,60 @@
+/*
+ *  ebt_pkttype
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_pkttype.h>
+#include <linux/module.h>
+
+static int ebt_filter_pkttype(const struct sk_buff *skb,
+   const struct net_device *in,
+   const struct net_device *out,
+   const void *data,
+   unsigned int datalen)
+{
+	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+	return (skb->pkt_type != info->pkt_type) ^ info->invert;
+}
+
+static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
+		return -EINVAL;
+	if (info->invert != 0 && info->invert != 1)
+		return -EINVAL;
+	/* Allow any pkt_type value */
+	return 0;
+}
+
+static struct ebt_match filter_pkttype =
+{
+	.name		= EBT_PKTTYPE_MATCH,
+	.match		= ebt_filter_pkttype,
+	.check		= ebt_pkttype_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_pkttype);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_pkttype);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_redirect.c b/kernel/linux/net/bridge/netfilter/ebt_redirect.c
new file mode 100644
index 0000000..661165e
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_redirect.c
@@ -0,0 +1,71 @@
+/*
+ *  ebt_redirect
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_redirect.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+	if (hooknr != NF_BR_BROUTING)
+		memcpy((**pskb).mac.ethernet->h_dest,
+		   in->br_port->br->dev.dev_addr, ETH_ALEN);
+	else {
+		memcpy((**pskb).mac.ethernet->h_dest,
+		   in->dev_addr, ETH_ALEN);
+		(*pskb)->pkt_type = PACKET_HOST;
+	}
+	return info->target;
+}
+
+static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target redirect_target =
+{
+	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
+	ebt_target_redirect_check, NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&redirect_target);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&redirect_target);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_snat.c b/kernel/linux/net/bridge/netfilter/ebt_snat.c
new file mode 100644
index 0000000..a327a1d
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_snat.c
@@ -0,0 +1,64 @@
+/*
+ *  ebt_snat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+
+static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
+	   ETH_ALEN * sizeof(unsigned char));
+	return info->target;
+}
+
+static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (strcmp(tablename, "nat"))
+		return -EINVAL;
+	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target snat =
+{
+	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
+	NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&snat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&snat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_stp.c b/kernel/linux/net/bridge/netfilter/ebt_stp.c
new file mode 100644
index 0000000..fcea9d3
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_stp.c
@@ -0,0 +1,191 @@
+/*
+ *  ebt_stp
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *	Stephen Hemminger <shemminger@osdl.org>
+ *
+ *  June, 2003
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_stp.h>
+#include <linux/module.h>
+
+#define BPDU_TYPE_CONFIG 0
+#define BPDU_TYPE_TCN 0x80
+
+struct stp_header {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint8_t ctrl;
+	uint8_t pid;
+	uint8_t vers;
+	uint8_t type;
+};
+
+struct stp_config_pdu {
+	uint8_t flags;
+	uint8_t root[8];
+	uint8_t root_cost[4];
+	uint8_t sender[8];
+	uint8_t port[2];
+	uint8_t msg_age[2];
+	uint8_t max_age[2];
+	uint8_t hello_time[2];
+	uint8_t forward_delay[2];
+};
+
+#define NR16(p) (p[0] << 8 | p[1])
+#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
+
+static int ebt_filter_config(struct ebt_stp_info *info,
+   struct stp_config_pdu *stpc)
+{
+	struct ebt_stp_config_info *c;
+	uint16_t v16;
+	uint32_t v32;
+	int verdict, i;
+
+	c = &info->config;
+	if ((info->bitmask & EBT_STP_FLAGS) &&
+	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_STP_ROOTPRIO) {
+		v16 = NR16(stpc->root);
+		if (FWINV(v16 < c->root_priol ||
+		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_ROOTADDR) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
+			           c->root_addrmsk[i];
+		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_ROOTCOST) {
+		v32 = NR32(stpc->root_cost);
+		if (FWINV(v32 < c->root_costl ||
+		    v32 > c->root_costu, EBT_STP_ROOTCOST))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_SENDERPRIO) {
+		v16 = NR16(stpc->sender);
+		if (FWINV(v16 < c->sender_priol ||
+		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_SENDERADDR) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
+			           c->sender_addrmsk[i];
+		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_PORT) {
+		v16 = NR16(stpc->port);
+		if (FWINV(v16 < c->portl ||
+		    v16 > c->portu, EBT_STP_PORT))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_MSGAGE) {
+		v16 = NR16(stpc->msg_age);
+		if (FWINV(v16 < c->msg_agel ||
+		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_MAXAGE) {
+		v16 = NR16(stpc->max_age);
+		if (FWINV(v16 < c->max_agel ||
+		    v16 > c->max_ageu, EBT_STP_MAXAGE))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_HELLOTIME) {
+		v16 = NR16(stpc->hello_time);
+		if (FWINV(v16 < c->hello_timel ||
+		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
+			return EBT_NOMATCH;
+	}
+	if (info->bitmask & EBT_STP_FWDD) {
+		v16 = NR16(stpc->forward_delay);
+		if (FWINV(v16 < c->forward_delayl ||
+		    v16 > c->forward_delayu, EBT_STP_FWDD))
+			return EBT_NOMATCH;
+	}
+	return EBT_MATCH;
+}
+
+static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
+	struct stp_header stph;
+	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
+	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
+		return EBT_NOMATCH;
+
+	/* The stp code only considers these */
+	if (memcmp(&stph, header, sizeof(header)))
+		return EBT_NOMATCH;
+
+	if (info->bitmask & EBT_STP_TYPE
+	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
+		return EBT_NOMATCH;
+
+	if (stph.type == BPDU_TYPE_CONFIG &&
+	    info->bitmask & EBT_STP_CONFIG_MASK) {
+		struct stp_config_pdu stpc;
+
+		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
+		    return EBT_NOMATCH;
+		return ebt_filter_config(info, &stpc);
+	}
+	return EBT_MATCH;
+}
+
+static int ebt_stp_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
+	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
+	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
+	    !(info->bitmask & EBT_STP_MASK))
+		return -EINVAL;
+	if (datalen != len)
+		return -EINVAL;
+	/* Make sure the match only receives stp frames */
+	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
+	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct ebt_match filter_stp =
+{
+	.name		= EBT_STP_MATCH,
+	.match		= ebt_filter_stp,
+	.check		= ebt_stp_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_stp);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_stp);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_vlan.c b/kernel/linux/net/bridge/netfilter/ebt_vlan.c
new file mode 100644
index 0000000..35abbcb
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_vlan.c
@@ -0,0 +1,259 @@
+/*
+ * Description: EBTables 802.1Q match extension kernelspace module.
+ * Authors: Nick Fedchik <nick@fedchik.org.ua>
+ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *    
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_vlan.h>
+
+static unsigned char debug;
+#define MODULE_VERSION "0.6"
+
+MODULE_PARM(debug, "0-1b");
+MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
+MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
+MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
+		   MODULE_VERSION);
+MODULE_LICENSE("GPL");
+
+
+#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
+#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
+
+/*
+ * Function description: ebt_filter_vlan() is main engine for 
+ * checking passed 802.1Q frame according to 
+ * the passed extension parameters (in the *data buffer)
+ * ebt_filter_vlan() is called after successfull check the rule params
+ * by ebt_check_vlan() function.
+ * Parameters:
+ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
+ * const void *data - pointer to passed extension parameters
+ * unsigned int datalen - length of passed *data buffer
+ * const struct net_device *in  -
+ * const struct net_device *out -
+ * const struct ebt_counter *c -
+ * Returned values:
+ * 0 - ok (all rule params matched)
+ * 1 - miss (rule params not acceptable to the parsed frame)
+ */
+static int
+ebt_filter_vlan(const struct sk_buff *skb,
+		const struct net_device *in,
+		const struct net_device *out,
+		const void *data, unsigned int datalen)
+{
+	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
+	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
+
+	unsigned short TCI;	/* Whole TCI, given from parsed frame */
+	unsigned short id;	/* VLAN ID, given from frame TCI */
+	unsigned char prio;	/* user_priority, given from frame TCI */
+	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
+
+	/*
+	 * Tag Control Information (TCI) consists of the following elements:
+	 * - User_priority. The user_priority field is three bits in length, 
+	 * interpreted as a binary number. 
+	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
+	 * (CFI) is a single bit flag value. Currently ignored.
+	 * - VLAN Identifier (VID). The VID is encoded as 
+	 * an unsigned binary number. 
+	 */
+	TCI = ntohs(frame->h_vlan_TCI);
+	id = TCI & VLAN_VID_MASK;
+	prio = (TCI >> 13) & 0x7;
+	encap = frame->h_vlan_encapsulated_proto;
+
+	/*
+	 * Checking VLAN Identifier (VID)
+	 */
+	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
+		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
+	}
+	/*
+	 * Checking user_priority
+	 */
+	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
+		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
+	}
+	/*
+	 * Checking Encapsulated Proto (Length/Type) field
+	 */
+	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
+		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
+	}
+	/*
+	 * All possible extension parameters was parsed.
+	 * If rule never returned by missmatch, then all ok.
+	 */
+	return 0;
+}
+
+/*
+ * Function description: ebt_vlan_check() is called when userspace 
+ * delivers the table entry to the kernel, 
+ * and to check that userspace doesn't give a bad table.
+ * Parameters:
+ * const char *tablename - table name string
+ * unsigned int hooknr - hook number
+ * const struct ebt_entry *e - ebtables entry basic set
+ * const void *data - pointer to passed extension parameters
+ * unsigned int datalen - length of passed *data buffer
+ * Returned values:
+ * 0 - ok (all delivered rule params are correct)
+ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
+ */
+static int
+ebt_check_vlan(const char *tablename,
+	       unsigned int hooknr,
+	       const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+
+	/*
+	 * Parameters buffer overflow check 
+	 */
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
+		DEBUG_MSG
+		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
+		     datalen, sizeof(struct ebt_vlan_info));
+		return -EINVAL;
+	}
+
+	/*
+	 * Is it 802.1Q frame checked?
+	 */
+	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
+		DEBUG_MSG
+		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+		     (unsigned short) ntohs(e->ethproto));
+		return -EINVAL;
+	}
+
+	/*
+	 * Check for bitmask range 
+	 * True if even one bit is out of mask
+	 */
+	if (info->bitmask & ~EBT_VLAN_MASK) {
+		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
+			  info->bitmask, EBT_VLAN_MASK);
+		return -EINVAL;
+	}
+
+	/*
+	 * Check for inversion flags range 
+	 */
+	if (info->invflags & ~EBT_VLAN_MASK) {
+		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
+			  info->invflags, EBT_VLAN_MASK);
+		return -EINVAL;
+	}
+
+	/*
+	 * Reserved VLAN ID (VID) values
+	 * -----------------------------
+	 * 0 - The null VLAN ID. 
+	 * 1 - The default Port VID (PVID)
+	 * 0x0FFF - Reserved for implementation use. 
+	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
+	 */
+	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
+		if (!!info->id) {	/* if id!=0 => check vid range */
+			if (info->id > VLAN_GROUP_ARRAY_LEN) {
+				DEBUG_MSG
+				    ("id %d is out of range (1-4096)\n",
+				     info->id);
+				return -EINVAL;
+			}
+			/*
+			 * Note: This is valid VLAN-tagged frame point.
+			 * Any value of user_priority are acceptable, 
+			 * but should be ignored according to 802.1Q Std.
+			 * So we just drop the prio flag. 
+			 */
+			info->bitmask &= ~EBT_VLAN_PRIO;
+		}
+		/*
+		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
+		 */
+	}
+
+	if (GET_BITMASK(EBT_VLAN_PRIO)) {
+		if ((unsigned char) info->prio > 7) {
+			DEBUG_MSG
+			    ("prio %d is out of range (0-7)\n",
+			     info->prio);
+			return -EINVAL;
+		}
+	}
+	/*
+	 * Check for encapsulated proto range - it is possible to be 
+	 * any value for u_short range.
+	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
+	 */
+	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
+		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
+			DEBUG_MSG
+			    ("encap frame length %d is less than minimal\n",
+			     ntohs(info->encap));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static struct ebt_match filter_vlan = {
+	{NULL, NULL},
+	EBT_VLAN_MATCH,
+	ebt_filter_vlan,
+	ebt_check_vlan,
+	NULL,
+	THIS_MODULE
+};
+
+/*
+ * Module initialization function.
+ */
+static int __init init(void)
+{
+	DEBUG_MSG("ebtables 802.1Q extension module v"
+		  MODULE_VERSION "\n");
+	DEBUG_MSG("module debug=%d\n", !!debug);
+	return ebt_register_match(&filter_vlan);
+}
+
+/*
+ * Module "finalization" function
+ */
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_vlan);
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_NO_SYMBOLS;
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_broute.c b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
new file mode 100644
index 0000000..ea443bb
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
@@ -0,0 +1,79 @@
+/*
+ *  ebtable_broute
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  This table lets you choose between routing and bridging for frames
+ *  entering on a bridge enslaved nic. This table is traversed before any
+ *  other ebtables table. See net/bridge/br_input.c.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#include <linux/if_bridge.h>
+#include <linux/brlock.h>
+
+// EBT_ACCEPT means the frame will be bridged
+// EBT_DROP means the frame will be routed
+static struct ebt_entries initial_chain =
+  {0, "BROUTING", 0, EBT_ACCEPT, 0};
+
+static struct ebt_replace initial_table =
+{
+  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
+  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~(1 << NF_BR_BROUTING))
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table broute_table =
+{
+  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
+  RW_LOCK_UNLOCKED, check, NULL
+};
+
+static int ebt_broute(struct sk_buff **pskb)
+{
+	int ret;
+
+	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
+	   &broute_table);
+	if (ret == NF_DROP)
+		return 1; // route it
+	return 0; // bridge it
+}
+
+static int __init init(void)
+{
+	int ret;
+
+	ret = ebt_register_table(&broute_table);
+	if (ret < 0)
+		return ret;
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	// see br_input.c
+	br_should_route_hook = ebt_broute;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	br_should_route_hook = NULL;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	ebt_unregister_table(&broute_table);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_filter.c b/kernel/linux/net/bridge/netfilter/ebtable_filter.c
new file mode 100644
index 0000000..9b7c0f9
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_filter.c
@@ -0,0 +1,90 @@
+/*
+ *  ebtable_filter
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+   (1 << NF_BR_LOCAL_OUT))
+
+static struct ebt_entries initial_chains[] =
+{
+  {0, "INPUT", 0, EBT_ACCEPT, 0},
+  {0, "FORWARD", 0, EBT_ACCEPT, 0},
+  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
+};
+
+static struct ebt_replace initial_table =
+{
+  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
+    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~FILTER_VALID_HOOKS)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table frame_filter =
+{ 
+  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
+  RW_LOCK_UNLOCKED, check, NULL
+};
+
+static unsigned int
+ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
+   const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_filter);
+}
+
+static struct nf_hook_ops ebt_ops_filter[] = {
+	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
+	   NF_BR_PRI_FILTER_BRIDGED},
+	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
+	   NF_BR_PRI_FILTER_BRIDGED},
+	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
+	   NF_BR_PRI_FILTER_OTHER}
+};
+
+static int __init init(void)
+{
+	int i, j, ret;
+
+	ret = ebt_register_table(&frame_filter);
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
+		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
+			goto cleanup;
+	return ret;
+cleanup:
+	for (j = 0; j < i; j++)
+		nf_unregister_hook(&ebt_ops_filter[j]);
+	ebt_unregister_table(&frame_filter);
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
+		nf_unregister_hook(&ebt_ops_filter[i]);
+	ebt_unregister_table(&frame_filter);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebtable_nat.c b/kernel/linux/net/bridge/netfilter/ebtable_nat.c
new file mode 100644
index 0000000..f687c5a
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_nat.c
@@ -0,0 +1,96 @@
+/*
+ *  ebtable_nat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+   (1 << NF_BR_POST_ROUTING))
+
+static struct ebt_entries initial_chains[] =
+{
+  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
+  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
+  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
+};
+
+static struct ebt_replace initial_table =
+{
+  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
+    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~NAT_VALID_HOOKS)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table frame_nat =
+{
+  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
+  RW_LOCK_UNLOCKED, check, NULL
+};
+
+static unsigned int
+ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static unsigned int
+ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static struct nf_hook_ops ebt_ops_nat[] = {
+	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
+	   NF_BR_PRI_NAT_DST_OTHER},
+	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
+	   NF_BR_PRI_NAT_SRC},
+	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
+	   NF_BR_PRI_NAT_DST_BRIDGED},
+};
+
+static int __init init(void)
+{
+	int i, ret, j;
+
+	ret = ebt_register_table(&frame_nat);
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
+		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
+			goto cleanup;
+	return ret;
+cleanup:
+	for (j = 0; j < i; j++)
+		nf_unregister_hook(&ebt_ops_nat[j]);
+	ebt_unregister_table(&frame_nat);
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
+		nf_unregister_hook(&ebt_ops_nat[i]);
+	ebt_unregister_table(&frame_nat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/bridge/netfilter/ebtables.c b/kernel/linux/net/bridge/netfilter/ebtables.c
new file mode 100644
index 0000000..9877321
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtables.c
@@ -0,0 +1,1490 @@
+/*
+ *  ebtables
+ *
+ *  Author:
+ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
+ *
+ *  ebtables.c,v 2.0, July, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+// used for print_string
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <net/sock.h>
+// needed for logical [in,out]-dev filtering
+#include "../br_private.h"
+
+// list_named_find
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0 // use this for remote debugging
+// Copyright (C) 1998 by Ori Pomerantz
+// Print the string to the appropriate tty, the one
+// the current task uses
+static void print_string(char *str)
+{
+	struct tty_struct *my_tty;
+
+	/* The tty for the current task */
+	my_tty = current->tty;
+	if (my_tty != NULL) {
+		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
+		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
+	}
+}
+
+#define BUGPRINT(args) print_string(args);
+#else
+#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+                                         "report to author: "format, ## args)
+// #define BUGPRINT(format, args...)
+#endif
+#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
+                                         ": out of memory: "format, ## args)
+// #define MEMPRINT(format, args...)
+
+
+
+// Each cpu has its own set of counters, so there is no need for write_lock in
+// the softirq
+// For reading or updating the counters, the user context needs to
+// get a write_lock
+
+// The size of each set of counters is altered to get cache alignment
+#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
+#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
+#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
+   COUNTER_OFFSET(n) * cpu))
+
+
+
+static DECLARE_MUTEX(ebt_mutex);
+static LIST_HEAD(ebt_tables);
+static LIST_HEAD(ebt_targets);
+static LIST_HEAD(ebt_matches);
+static LIST_HEAD(ebt_watchers);
+
+static struct ebt_target ebt_standard_target =
+{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+
+static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+   const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out)
+{
+	w->u.watcher->watcher(skb, in, out, w->data,
+	   w->watcher_size);
+	// watchers don't give a verdict
+	return 0;
+}
+
+static inline int ebt_do_match (struct ebt_entry_match *m,
+   const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out)
+{
+	return m->u.match->match(skb, in, out, m->data,
+	   m->match_size);
+}
+
+static inline int ebt_dev_check(char *entry, const struct net_device *device)
+{
+	if (*entry == '\0')
+		return 0;
+	if (!device)
+		return 1;
+	return !!strcmp(entry, device->name);
+}
+
+#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+// process standard matches
+static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+   const struct net_device *in, const struct net_device *out)
+{
+	int verdict, i;
+
+	if (e->bitmask & EBT_802_3) {
+		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+			return 1;
+	} else if (!(e->bitmask & EBT_NOPROTO) &&
+	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+		return 1;
+
+	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+		return 1;
+	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+		return 1;
+	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
+	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+		return 1;
+	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
+	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+		return 1;
+
+	if (e->bitmask & EBT_SOURCEMAC) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
+			   e->sourcemsk[i];
+		if (FWINV2(verdict != 0, EBT_ISOURCE) )
+			return 1;
+	}
+	if (e->bitmask & EBT_DESTMAC) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
+			   e->destmsk[i];
+		if (FWINV2(verdict != 0, EBT_IDEST) )
+			return 1;
+	}
+	return 0;
+}
+
+// Do some firewalling
+unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   struct ebt_table *table)
+{
+	int i, nentries;
+	struct ebt_entry *point;
+	struct ebt_counter *counter_base, *cb_base;
+	struct ebt_entry_target *t;
+	int verdict, sp = 0;
+	struct ebt_chainstack *cs;
+	struct ebt_entries *chaininfo;
+	char *base;
+	struct ebt_table_info *private = table->private;
+
+	read_lock_bh(&table->lock);
+	cb_base = COUNTER_BASE(private->counters, private->nentries,
+	   cpu_number_map(smp_processor_id()));
+	if (private->chainstack)
+		cs = private->chainstack[cpu_number_map(smp_processor_id())];
+	else
+		cs = NULL;
+	chaininfo = private->hook_entry[hook];
+	nentries = private->hook_entry[hook]->nentries;
+	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+	// base for chain jumps
+	base = private->entries;
+	i = 0;
+	while (i < nentries) {
+		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
+			goto letscontinue;
+
+		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+			goto letscontinue;
+
+		// increase counter
+		(*(counter_base + i)).pcnt++;
+		(*(counter_base + i)).bcnt+=(**pskb).len;
+
+		// these should only watch: not modify, nor tell us
+		// what to do with the packet
+		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+		   out);
+
+		t = (struct ebt_entry_target *)
+		   (((char *)point) + point->target_offset);
+		// standard target
+		if (!t->u.target->target)
+			verdict = ((struct ebt_standard_target *)t)->verdict;
+		else
+			verdict = t->u.target->target(pskb, hook,
+			   in, out, t->data, t->target_size);
+		if (verdict == EBT_ACCEPT) {
+			read_unlock_bh(&table->lock);
+			return NF_ACCEPT;
+		}
+		if (verdict == EBT_DROP) {
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+		if (verdict == EBT_RETURN) {
+letsreturn:
+#ifdef CONFIG_NETFILTER_DEBUG
+			if (sp == 0) {
+				BUGPRINT("RETURN on base chain");
+				// act like this is EBT_CONTINUE
+				goto letscontinue;
+			}
+#endif
+			sp--;
+			// put all the local variables right
+			i = cs[sp].n;
+			chaininfo = cs[sp].chaininfo;
+			nentries = chaininfo->nentries;
+			point = cs[sp].e;
+			counter_base = cb_base +
+			   chaininfo->counter_offset;
+			continue;
+		}
+		if (verdict == EBT_CONTINUE)
+			goto letscontinue;
+#ifdef CONFIG_NETFILTER_DEBUG
+		if (verdict < 0) {
+			BUGPRINT("bogus standard verdict\n");
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+#endif
+		// jump to a udc
+		cs[sp].n = i + 1;
+		cs[sp].chaininfo = chaininfo;
+		cs[sp].e = (struct ebt_entry *)
+		   (((char *)point) + point->next_offset);
+		i = 0;
+		chaininfo = (struct ebt_entries *) (base + verdict);
+#ifdef CONFIG_NETFILTER_DEBUG
+		if (chaininfo->distinguisher) {
+			BUGPRINT("jump to non-chain\n");
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+#endif
+		nentries = chaininfo->nentries;
+		point = (struct ebt_entry *)chaininfo->data;
+		counter_base = cb_base + chaininfo->counter_offset;
+		sp++;
+		continue;
+letscontinue:
+		point = (struct ebt_entry *)
+		   (((char *)point) + point->next_offset);
+		i++;
+	}
+
+	// I actually like this :)
+	if (chaininfo->policy == EBT_RETURN)
+		goto letsreturn;
+	if (chaininfo->policy == EBT_ACCEPT) {
+		read_unlock_bh(&table->lock);
+		return NF_ACCEPT;
+	}
+	read_unlock_bh(&table->lock);
+	return NF_DROP;
+}
+
+// If it succeeds, returns element and locks mutex
+static inline void *
+find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
+   struct semaphore *mutex)
+{
+	void *ret;
+
+	*error = down_interruptible(mutex);
+	if (*error != 0)
+		return NULL;
+
+	ret = list_named_find(head, name);
+	if (!ret) {
+		*error = -ENOENT;
+		up(mutex);
+	}
+	return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
+   int *error, struct semaphore *mutex)
+{
+	void *ret;
+
+	ret = find_inlist_lock_noload(head, name, error, mutex);
+	if (!ret) {
+		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
+		strcpy(modulename, prefix);
+		strcat(modulename, name);
+		request_module(modulename);
+		ret = find_inlist_lock_noload(head, name, error, mutex);
+	}
+	return ret;
+}
+#endif
+
+static inline struct ebt_table *
+find_table_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+}
+
+static inline struct ebt_match *
+find_match_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_watcher *
+find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_target *
+find_target_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
+}
+
+static inline int
+ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+   const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+	struct ebt_match *match;
+	int ret;
+
+	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+	   ((char *)e) + e->watchers_offset)
+		return -EINVAL;
+	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+	if (!match)
+		return ret;
+	m->u.match = match;
+	if (match->me)
+		__MOD_INC_USE_COUNT(match->me);
+	up(&ebt_mutex);
+	if (match->check &&
+	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+		BUGPRINT("match->check failed\n");
+		if (match->me)
+			__MOD_DEC_USE_COUNT(match->me);
+		return -EINVAL;
+	}
+	(*cnt)++;
+	return 0;
+}
+
+static inline int
+ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+   const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+	struct ebt_watcher *watcher;
+	int ret;
+
+	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+	   ((char *)e) + e->target_offset)
+		return -EINVAL;
+	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+	if (!watcher)
+		return ret;
+	w->u.watcher = watcher;
+	if (watcher->me)
+		__MOD_INC_USE_COUNT(watcher->me);
+	up(&ebt_mutex);
+	if (watcher->check &&
+	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+		BUGPRINT("watcher->check failed\n");
+		if (watcher->me)
+			__MOD_DEC_USE_COUNT(watcher->me);
+		return -EINVAL;
+	}
+	(*cnt)++;
+	return 0;
+}
+
+// this one is very careful, as it is the first function
+// to parse the userspace data
+static inline int
+ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+   struct ebt_table_info *newinfo, char *base, char *limit,
+   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+{
+	int i;
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if ( (char *)hook_entries[i] - base ==
+		   (char *)e - newinfo->entries)
+			break;
+	}
+	// beginning of a new chain
+	// if i == NF_BR_NUMHOOKS it must be a user defined chain
+	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+			// we make userspace set this right,
+			// so there is no misunderstanding
+			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+			         "in distinguisher\n");
+			return -EINVAL;
+		}
+		// this checks if the previous chain has as many entries
+		// as it said it has
+		if (*n != *cnt) {
+			BUGPRINT("nentries does not equal the nr of entries "
+		                 "in the chain\n");
+			return -EINVAL;
+		}
+		// before we look at the struct, be sure it is not too big
+		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
+		   > limit) {
+			BUGPRINT("entries_size too small\n");
+			return -EINVAL;
+		}
+		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+			// only RETURN from udc
+			if (i != NF_BR_NUMHOOKS ||
+			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
+				BUGPRINT("bad policy\n");
+				return -EINVAL;
+			}
+		}
+		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
+			(*udc_cnt)++;
+		else
+			newinfo->hook_entry[i] = (struct ebt_entries *)e;
+		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
+			BUGPRINT("counter_offset != totalcnt");
+			return -EINVAL;
+		}
+		*n = ((struct ebt_entries *)e)->nentries;
+		*cnt = 0;
+		return 0;
+	}
+	// a plain old entry, heh
+	if (sizeof(struct ebt_entry) > e->watchers_offset ||
+	   e->watchers_offset > e->target_offset ||
+	   e->target_offset >= e->next_offset) {
+		BUGPRINT("entry offsets not in right order\n");
+		return -EINVAL;
+	}
+	// this is not checked anywhere else
+	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
+		BUGPRINT("target size too small\n");
+		return -EINVAL;
+	}
+
+	(*cnt)++;
+	(*totalcnt)++;
+	return 0;
+}
+
+struct ebt_cl_stack
+{
+	struct ebt_chainstack cs;
+	int from;
+	unsigned int hookmask;
+};
+
+// we need these positions to check that the jumps to a different part of the
+// entries is a jump to the beginning of a new chain.
+static inline int
+ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
+   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
+   struct ebt_cl_stack *udc)
+{
+	int i;
+
+	// we're only interested in chain starts
+	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+		return 0;
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
+			break;
+	}
+	// only care about udc
+	if (i != NF_BR_NUMHOOKS)
+		return 0;
+
+	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
+	// these initialisations are depended on later in check_chainloops()
+	udc[*n].cs.n = 0;
+	udc[*n].hookmask = 0;
+
+	(*n)++;
+	return 0;
+}
+
+static inline int
+ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+	if (m->u.match->destroy)
+		m->u.match->destroy(m->data, m->match_size);
+	if (m->u.match->me)
+		__MOD_DEC_USE_COUNT(m->u.match->me);
+
+	return 0;
+}
+
+static inline int
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+	if (w->u.watcher->destroy)
+		w->u.watcher->destroy(w->data, w->watcher_size);
+	if (w->u.watcher->me)
+		__MOD_DEC_USE_COUNT(w->u.watcher->me);
+
+	return 0;
+}
+
+static inline int
+ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+{
+	struct ebt_entry_target *t;
+
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+	// we're done
+	if (cnt && (*cnt)-- == 0)
+		return 1;
+	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	if (t->u.target->destroy)
+		t->u.target->destroy(t->data, t->target_size);
+	if (t->u.target->me)
+		__MOD_DEC_USE_COUNT(t->u.target->me);
+
+	return 0;
+}
+
+static inline int
+ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+   const char *name, unsigned int *cnt, unsigned int valid_hooks,
+   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+{
+	struct ebt_entry_target *t;
+	struct ebt_target *target;
+	unsigned int i, j, hook = 0, hookmask = 0;
+	int ret;
+
+	// Don't mess with the struct ebt_entries
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+
+	if (e->bitmask & ~EBT_F_MASK) {
+		BUGPRINT("Unknown flag for bitmask\n");
+		return -EINVAL;
+	}
+	if (e->invflags & ~EBT_INV_MASK) {
+		BUGPRINT("Unknown flag for inv bitmask\n");
+		return -EINVAL;
+	}
+	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+		BUGPRINT("NOPROTO & 802_3 not allowed\n");
+		return -EINVAL;
+	}
+	// what hook do we belong to?
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if ((char *)newinfo->hook_entry[i] < (char *)e)
+			hook = i;
+		else
+			break;
+	}
+	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
+	// a base chain
+	if (i < NF_BR_NUMHOOKS)
+		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+	else {
+		for (i = 0; i < udc_cnt; i++)
+			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+				break;
+		if (i == 0)
+			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+		else
+			hookmask = cl_s[i - 1].hookmask;
+	}
+	i = 0;
+	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+	if (ret != 0)
+		goto cleanup_matches;
+	j = 0;
+	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+	if (ret != 0)
+		goto cleanup_watchers;
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+	if (!target)
+		goto cleanup_watchers;
+	if (target->me)
+		__MOD_INC_USE_COUNT(target->me);
+	up(&ebt_mutex);
+
+	t->u.target = target;
+	if (t->u.target == &ebt_standard_target) {
+		if (e->target_offset + sizeof(struct ebt_standard_target) >
+		   e->next_offset) {
+			BUGPRINT("Standard target size too big\n");
+			ret = -EFAULT;
+			goto cleanup_watchers;
+		}
+		if (((struct ebt_standard_target *)t)->verdict <
+		   -NUM_STANDARD_TARGETS) {
+			BUGPRINT("Invalid standard target\n");
+			ret = -EFAULT;
+			goto cleanup_watchers;
+		}
+	} else if ((e->target_offset + t->target_size +
+	   sizeof(struct ebt_entry_target) > e->next_offset) ||
+	   (t->u.target->check &&
+	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+		if (t->u.target->me)
+			__MOD_DEC_USE_COUNT(t->u.target->me);
+		ret = -EFAULT;
+		goto cleanup_watchers;
+	}
+	(*cnt)++;
+	return 0;
+cleanup_watchers:
+	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+cleanup_matches:
+	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+	return ret;
+}
+
+// checks for loops and sets the hook mask for udc
+// the hook mask for udc tells us from which base chains the udc can be
+// accessed. This mask is a parameter to the check() functions of the extensions
+static int check_chainloops(struct ebt_entries *chain,
+   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
+   unsigned int hooknr, char *base)
+{
+	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
+	struct ebt_entry *e = (struct ebt_entry *)chain->data;
+	struct ebt_entry_target *t;
+
+	while (pos < nentries || chain_nr != -1) {
+		// end of udc, go back one 'recursion' step
+		if (pos == nentries) {
+			// put back values of the time when this chain was called
+			e = cl_s[chain_nr].cs.e;
+			if (cl_s[chain_nr].from != -1)
+				nentries =
+				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+			else
+				nentries = chain->nentries;
+			pos = cl_s[chain_nr].cs.n;
+			// make sure we won't see a loop that isn't one
+			cl_s[chain_nr].cs.n = 0;
+			chain_nr = cl_s[chain_nr].from;
+			if (pos == nentries)
+				continue;
+		}
+		t = (struct ebt_entry_target *)
+		   (((char *)e) + e->target_offset);
+		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+			goto letscontinue;
+		if (e->target_offset + sizeof(struct ebt_standard_target) >
+		   e->next_offset) {
+			BUGPRINT("Standard target size too big\n");
+			return -1;
+		}
+		verdict = ((struct ebt_standard_target *)t)->verdict;
+		if (verdict >= 0) { // jump to another chain
+			struct ebt_entries *hlp2 =
+			   (struct ebt_entries *)(base + verdict);
+			for (i = 0; i < udc_cnt; i++)
+				if (hlp2 == cl_s[i].cs.chaininfo)
+					break;
+			// bad destination or loop
+			if (i == udc_cnt) {
+				BUGPRINT("bad destination\n");
+				return -1;
+			}
+			if (cl_s[i].cs.n) {
+				BUGPRINT("loop\n");
+				return -1;
+			}
+			// this can't be 0, so the above test is correct
+			cl_s[i].cs.n = pos + 1;
+			pos = 0;
+			cl_s[i].cs.e = ((void *)e + e->next_offset);
+			e = (struct ebt_entry *)(hlp2->data);
+			nentries = hlp2->nentries;
+			cl_s[i].from = chain_nr;
+			chain_nr = i;
+			// this udc is accessible from the base chain for hooknr
+			cl_s[i].hookmask |= (1 << hooknr);
+			continue;
+		}
+letscontinue:
+		e = (void *)e + e->next_offset;
+		pos++;
+	}
+	return 0;
+}
+
+// do the parsing of the table/chains/entries/matches/watchers/targets, heh
+static int translate_table(struct ebt_replace *repl,
+   struct ebt_table_info *newinfo)
+{
+	unsigned int i, j, k, udc_cnt;
+	int ret;
+	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
+
+	i = 0;
+	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+		i++;
+	if (i == NF_BR_NUMHOOKS) {
+		BUGPRINT("No valid hooks specified\n");
+		return -EINVAL;
+	}
+	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+		BUGPRINT("Chains don't start at beginning\n");
+		return -EINVAL;
+	}
+	// make sure chains are ordered after each other in same order
+	// as their corresponding hooks
+	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
+		if (!(repl->valid_hooks & (1 << j)))
+			continue;
+		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+			BUGPRINT("Hook order must be followed\n");
+			return -EINVAL;
+		}
+		i = j;
+	}
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		newinfo->hook_entry[i] = NULL;
+
+	newinfo->entries_size = repl->entries_size;
+	newinfo->nentries = repl->nentries;
+
+	// do some early checkings and initialize some things
+	i = 0; // holds the expected nr. of entries for the chain
+	j = 0; // holds the up to now counted entries for the chain
+	k = 0; // holds the total nr. of entries, should equal
+	       // newinfo->nentries afterwards
+	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
+	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+	   &udc_cnt, repl->valid_hooks);
+
+	if (ret != 0)
+		return ret;
+
+	if (i != j) {
+		BUGPRINT("nentries does not equal the nr of entries in the "
+		         "(last) chain\n");
+		return -EINVAL;
+	}
+	if (k != newinfo->nentries) {
+		BUGPRINT("Total nentries is wrong\n");
+		return -EINVAL;
+	}
+
+	// check if all valid hooks have a chain
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if (newinfo->hook_entry[i] == NULL &&
+		   (repl->valid_hooks & (1 << i))) {
+			BUGPRINT("Valid hook without chain\n");
+			return -EINVAL;
+		}
+	}
+
+	// Get the location of the udc, put them in an array
+	// While we're at it, allocate the chainstack
+	if (udc_cnt) {
+		// this will get free'd in do_replace()/ebt_register_table()
+		// if an error occurs
+		newinfo->chainstack = (struct ebt_chainstack **)
+		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
+		if (!newinfo->chainstack)
+			return -ENOMEM;
+		for (i = 0; i < smp_num_cpus; i++) {
+			newinfo->chainstack[i] =
+			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
+			if (!newinfo->chainstack[i]) {
+				while (i)
+					vfree(newinfo->chainstack[--i]);
+				vfree(newinfo->chainstack);
+				newinfo->chainstack = NULL;
+				return -ENOMEM;
+			}
+		}
+
+		cl_s = (struct ebt_cl_stack *)
+		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+		if (!cl_s)
+			return -ENOMEM;
+		i = 0; // the i'th udc
+		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
+		   repl->valid_hooks, cl_s);
+		// sanity check
+		if (i != udc_cnt) {
+			BUGPRINT("i != udc_cnt\n");
+			vfree(cl_s);
+			return -EFAULT;
+		}
+	}
+
+	// Check for loops
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (repl->valid_hooks & (1 << i))
+			if (check_chainloops(newinfo->hook_entry[i],
+			   cl_s, udc_cnt, i, newinfo->entries)) {
+				if (cl_s)
+					vfree(cl_s);
+				return -EINVAL;
+			}
+
+	// we now know the following (along with E=mc²):
+	// - the nr of entries in each chain is right
+	// - the size of the allocated space is right
+	// - all valid hooks have a corresponding chain
+	// - there are no loops
+	// - wrong data can still be on the level of a single entry
+	// - could be there are jumps to places that are not the
+	//   beginning of a chain. This can only occur in chains that
+	//   are not accessible from any base chains, so we don't care.
+
+	// used to know what we need to clean up if something goes wrong
+	i = 0;
+	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
+	   cl_s, udc_cnt);
+	if (ret != 0) {
+		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+		   ebt_cleanup_entry, &i);
+	}
+	if (cl_s)
+		vfree(cl_s);
+	return ret;
+}
+
+// called under write_lock
+static void get_counters(struct ebt_counter *oldcounters,
+   struct ebt_counter *counters, unsigned int nentries)
+{
+	int i, cpu;
+	struct ebt_counter *counter_base;
+
+	// counters of cpu 0
+	memcpy(counters, oldcounters,
+	   sizeof(struct ebt_counter) * nentries);
+	// add other counters to those of cpu 0
+	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
+		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+		for (i = 0; i < nentries; i++) {
+			counters[i].pcnt += counter_base[i].pcnt;
+			counters[i].bcnt += counter_base[i].bcnt;
+		}
+	}
+}
+
+// replace the table
+static int do_replace(void *user, unsigned int len)
+{
+	int ret, i, countersize;
+	struct ebt_table_info *newinfo;
+	struct ebt_replace tmp;
+	struct ebt_table *t;
+	struct ebt_counter *counterstmp = NULL;
+	// used to be able to unlock earlier
+	struct ebt_table_info *table;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	if (len != sizeof(tmp) + tmp.entries_size) {
+		BUGPRINT("Wrong len argument\n");
+		return -EINVAL;
+	}
+
+	if (tmp.entries_size == 0) {
+		BUGPRINT("Entries_size never zero\n");
+		return -EINVAL;
+	}
+	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
+	newinfo = (struct ebt_table_info *)
+	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+	if (!newinfo)
+		return -ENOMEM;
+
+	if (countersize)
+		memset(newinfo->counters, 0, countersize);
+
+	newinfo->entries = (char *)vmalloc(tmp.entries_size);
+	if (!newinfo->entries) {
+		ret = -ENOMEM;
+		goto free_newinfo;
+	}
+	if (copy_from_user(
+	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+		BUGPRINT("Couldn't copy entries from userspace\n");
+		ret = -EFAULT;
+		goto free_entries;
+	}
+
+	// the user wants counters back
+	// the check on the size is done later, when we have the lock
+	if (tmp.num_counters) {
+		counterstmp = (struct ebt_counter *)
+		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
+		if (!counterstmp) {
+			ret = -ENOMEM;
+			goto free_entries;
+		}
+	}
+	else
+		counterstmp = NULL;
+
+	// this can get initialized by translate_table()
+	newinfo->chainstack = NULL;
+	ret = translate_table(&tmp, newinfo);
+
+	if (ret != 0)
+		goto free_counterstmp;
+
+	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+	if (!t)
+		goto free_iterate;
+
+	// the table doesn't like it
+	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+		goto free_unlock;
+
+	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+		BUGPRINT("Wrong nr. of counters requested\n");
+		ret = -EINVAL;
+		goto free_unlock;
+	}
+
+	// we have the mutex lock, so no danger in reading this pointer
+	table = t->private;
+	// we need an atomic snapshot of the counters
+	write_lock_bh(&t->lock);
+	if (tmp.num_counters)
+		get_counters(t->private->counters, counterstmp,
+		   t->private->nentries);
+
+	t->private = newinfo;
+	write_unlock_bh(&t->lock);
+	up(&ebt_mutex);
+	// So, a user can change the chains while having messed up her counter
+	// allocation. Only reason why this is done is because this way the lock
+	// is held only once, while this doesn't bring the kernel into a
+	// dangerous state.
+	if (tmp.num_counters &&
+	   copy_to_user(tmp.counters, counterstmp,
+	   tmp.num_counters * sizeof(struct ebt_counter))) {
+		BUGPRINT("Couldn't copy counters to userspace\n");
+		ret = -EFAULT;
+	}
+	else
+		ret = 0;
+
+	// decrease module count and free resources
+	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
+	   ebt_cleanup_entry, NULL);
+
+	vfree(table->entries);
+	if (table->chainstack) {
+		for (i = 0; i < smp_num_cpus; i++)
+			vfree(table->chainstack[i]);
+		vfree(table->chainstack);
+	}
+	vfree(table);
+
+	if (counterstmp)
+		vfree(counterstmp);
+	return ret;
+
+free_unlock:
+	up(&ebt_mutex);
+free_iterate:
+	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_cleanup_entry, NULL);
+free_counterstmp:
+	if (counterstmp)
+		vfree(counterstmp);
+	// can be initialized in translate_table()
+	if (newinfo->chainstack) {
+		for (i = 0; i < smp_num_cpus; i++)
+			vfree(newinfo->chainstack[i]);
+		vfree(newinfo->chainstack);
+	}
+free_entries:
+	if (newinfo->entries)
+		vfree(newinfo->entries);
+free_newinfo:
+	if (newinfo)
+		vfree(newinfo);
+	return ret;
+}
+
+int ebt_register_target(struct ebt_target *target)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_targets, target)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+void ebt_unregister_target(struct ebt_target *target)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_targets, target);
+	up(&ebt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_match(struct ebt_match *match)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_matches, match)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+void ebt_unregister_match(struct ebt_match *match)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_matches, match);
+	up(&ebt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_watcher(struct ebt_watcher *watcher)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_watchers, watcher)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+void ebt_unregister_watcher(struct ebt_watcher *watcher)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_watchers, watcher);
+	up(&ebt_mutex);
+	MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_table(struct ebt_table *table)
+{
+	struct ebt_table_info *newinfo;
+	int ret, i, countersize;
+
+	if (!table || !table->table ||!table->table->entries ||
+	    table->table->entries_size == 0 ||
+	    table->table->counters || table->private) {
+		BUGPRINT("Bad table data for ebt_register_table!!!\n");
+		return -EINVAL;
+	}
+
+	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
+	newinfo = (struct ebt_table_info *)
+	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+	ret = -ENOMEM;
+	if (!newinfo)
+		return -ENOMEM;
+
+	newinfo->entries = (char *)vmalloc(table->table->entries_size);
+	if (!(newinfo->entries))
+		goto free_newinfo;
+
+	memcpy(newinfo->entries, table->table->entries,
+	   table->table->entries_size);
+
+	if (countersize)
+		memset(newinfo->counters, 0, countersize);
+
+	// fill in newinfo and parse the entries
+	newinfo->chainstack = NULL;
+	ret = translate_table(table->table, newinfo);
+	if (ret != 0) {
+		BUGPRINT("Translate_table failed\n");
+		goto free_chainstack;
+	}
+
+	if (table->check && table->check(newinfo, table->valid_hooks)) {
+		BUGPRINT("The table doesn't like its own initial data, lol\n");
+		return -EINVAL;
+	}
+
+	table->private = newinfo;
+	table->lock = RW_LOCK_UNLOCKED;
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		goto free_chainstack;
+
+	if (list_named_find(&ebt_tables, table->name)) {
+		ret = -EEXIST;
+		BUGPRINT("Table name already exists\n");
+		goto free_unlock;
+	}
+
+	list_prepend(&ebt_tables, table);
+	up(&ebt_mutex);
+	MOD_INC_USE_COUNT;
+	return 0;
+free_unlock:
+	up(&ebt_mutex);
+free_chainstack:
+	if (newinfo->chainstack) {
+		for (i = 0; i < smp_num_cpus; i++)
+			vfree(newinfo->chainstack[i]);
+		vfree(newinfo->chainstack);
+	}
+	vfree(newinfo->entries);
+free_newinfo:
+	vfree(newinfo);
+	return ret;
+}
+
+void ebt_unregister_table(struct ebt_table *table)
+{
+	int i;
+
+	if (!table) {
+		BUGPRINT("Request to unregister NULL table!!!\n");
+		return;
+	}
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_tables, table);
+	up(&ebt_mutex);
+	EBT_ENTRY_ITERATE(table->private->entries,
+	   table->private->entries_size, ebt_cleanup_entry, NULL);
+	if (table->private->entries)
+		vfree(table->private->entries);
+	if (table->private->chainstack) {
+		for (i = 0; i < smp_num_cpus; i++)
+			vfree(table->private->chainstack[i]);
+		vfree(table->private->chainstack);
+	}
+	vfree(table->private);
+	MOD_DEC_USE_COUNT;
+}
+
+// userspace just supplied us with counters
+static int update_counters(void *user, unsigned int len)
+{
+	int i, ret;
+	struct ebt_counter *tmp;
+	struct ebt_replace hlp;
+	struct ebt_table *t;
+
+	if (copy_from_user(&hlp, user, sizeof(hlp)))
+		return -EFAULT;
+
+	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
+		return -EINVAL;
+	if (hlp.num_counters == 0)
+		return -EINVAL;
+
+	if ( !(tmp = (struct ebt_counter *)
+	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+		MEMPRINT("Update_counters && nomemory\n");
+		return -ENOMEM;
+	}
+
+	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+	if (!t)
+		goto free_tmp;
+
+	if (hlp.num_counters != t->private->nentries) {
+		BUGPRINT("Wrong nr of counters\n");
+		ret = -EINVAL;
+		goto unlock_mutex;
+	}
+
+	if ( copy_from_user(tmp, hlp.counters,
+	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
+		BUGPRINT("Updata_counters && !cfu\n");
+		ret = -EFAULT;
+		goto unlock_mutex;
+	}
+
+	// we want an atomic add of the counters
+	write_lock_bh(&t->lock);
+
+	// we add to the counters of the first cpu
+	for (i = 0; i < hlp.num_counters; i++) {
+		t->private->counters[i].pcnt += tmp[i].pcnt;
+		t->private->counters[i].bcnt += tmp[i].bcnt;
+	}
+
+	write_unlock_bh(&t->lock);
+	ret = 0;
+unlock_mutex:
+	up(&ebt_mutex);
+free_tmp:
+	vfree(tmp);
+	return ret;
+}
+
+static inline int ebt_make_matchname(struct ebt_entry_match *m,
+   char *base, char *ubase)
+{
+	char *hlp = ubase - base + (char *)m;
+	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
+   char *base, char *ubase)
+{
+	char *hlp = ubase - base + (char *)w;
+	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+{
+	int ret;
+	char *hlp;
+	struct ebt_entry_target *t;
+
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+
+	hlp = ubase - base + (char *)e + e->target_offset;
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	
+	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+	if (ret != 0)
+		return ret;
+	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
+	if (ret != 0)
+		return ret;
+	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+// called with ebt_mutex down
+static int copy_everything_to_user(struct ebt_table *t, void *user,
+   int *len, int cmd)
+{
+	struct ebt_replace tmp;
+	struct ebt_counter *counterstmp, *oldcounters;
+	unsigned int entries_size, nentries;
+	char *entries;
+
+	if (cmd == EBT_SO_GET_ENTRIES) {
+		entries_size = t->private->entries_size;
+		nentries = t->private->nentries;
+		entries = t->private->entries;
+		oldcounters = t->private->counters;
+	} else {
+		entries_size = t->table->entries_size;
+		nentries = t->table->nentries;
+		entries = t->table->entries;
+		oldcounters = t->table->counters;
+	}
+
+	if (copy_from_user(&tmp, user, sizeof(tmp))) {
+		BUGPRINT("Cfu didn't work\n");
+		return -EFAULT;
+	}
+
+	if (*len != sizeof(struct ebt_replace) + entries_size +
+	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
+		BUGPRINT("Wrong size\n");
+		return -EINVAL;
+	}
+
+	if (tmp.nentries != nentries) {
+		BUGPRINT("Nentries wrong\n");
+		return -EINVAL;
+	}
+
+	if (tmp.entries_size != entries_size) {
+		BUGPRINT("Wrong size\n");
+		return -EINVAL;
+	}
+
+	// userspace might not need the counters
+	if (tmp.num_counters) {
+		if (tmp.num_counters != nentries) {
+			BUGPRINT("Num_counters wrong\n");
+			return -EINVAL;
+		}
+		counterstmp = (struct ebt_counter *)
+		   vmalloc(nentries * sizeof(struct ebt_counter));
+		if (!counterstmp) {
+			MEMPRINT("Couldn't copy counters, out of memory\n");
+			return -ENOMEM;
+		}
+		write_lock_bh(&t->lock);
+		get_counters(oldcounters, counterstmp, nentries);
+		write_unlock_bh(&t->lock);
+
+		if (copy_to_user(tmp.counters, counterstmp,
+		   nentries * sizeof(struct ebt_counter))) {
+			BUGPRINT("Couldn't copy counters to userspace\n");
+			vfree(counterstmp);
+			return -EFAULT;
+		}
+		vfree(counterstmp);
+	}
+
+	if (copy_to_user(tmp.entries, entries, entries_size)) {
+		BUGPRINT("Couldn't copy entries to userspace\n");
+		return -EFAULT;
+	}
+	// set the match/watcher/target names right
+	return EBT_ENTRY_ITERATE(entries, entries_size,
+	   ebt_make_names, entries, tmp.entries);
+}
+
+static int do_ebt_set_ctl(struct sock *sk,
+	int cmd, void *user, unsigned int len)
+{
+	int ret;
+
+	switch(cmd) {
+	case EBT_SO_SET_ENTRIES:
+		ret = do_replace(user, len);
+		break;
+	case EBT_SO_SET_COUNTERS:
+		ret = update_counters(user, len);
+		break;
+	default:
+		ret = -EINVAL;
+  }
+	return ret;
+}
+
+static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+	int ret;
+	struct ebt_replace tmp;
+	struct ebt_table *t;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)))
+		return -EFAULT;
+
+	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+	if (!t)
+		return ret;
+
+	switch(cmd) {
+	case EBT_SO_GET_INFO:
+	case EBT_SO_GET_INIT_INFO:
+		if (*len != sizeof(struct ebt_replace)){
+			ret = -EINVAL;
+			up(&ebt_mutex);
+			break;
+		}
+		if (cmd == EBT_SO_GET_INFO) {
+			tmp.nentries = t->private->nentries;
+			tmp.entries_size = t->private->entries_size;
+			tmp.valid_hooks = t->valid_hooks;
+		} else {
+			tmp.nentries = t->table->nentries;
+			tmp.entries_size = t->table->entries_size;
+			tmp.valid_hooks = t->table->valid_hooks;
+		}
+		up(&ebt_mutex);
+		if (copy_to_user(user, &tmp, *len) != 0){
+			BUGPRINT("c2u Didn't work\n");
+			ret = -EFAULT;
+			break;
+		}
+		ret = 0;
+		break;
+
+	case EBT_SO_GET_ENTRIES:
+	case EBT_SO_GET_INIT_ENTRIES:
+		ret = copy_everything_to_user(t, user, len, cmd);
+		up(&ebt_mutex);
+		break;
+
+	default:
+		up(&ebt_mutex);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct nf_sockopt_ops ebt_sockopts =
+{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
+    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+};
+
+static int __init init(void)
+{
+	int ret;
+
+	down(&ebt_mutex);
+	list_named_insert(&ebt_targets, &ebt_standard_target);
+	up(&ebt_mutex);
+	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+		return ret;
+
+	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	nf_unregister_sockopt(&ebt_sockopts);
+	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
+}
+
+EXPORT_SYMBOL(ebt_register_table);
+EXPORT_SYMBOL(ebt_unregister_table);
+EXPORT_SYMBOL(ebt_register_match);
+EXPORT_SYMBOL(ebt_unregister_match);
+EXPORT_SYMBOL(ebt_register_watcher);
+EXPORT_SYMBOL(ebt_unregister_watcher);
+EXPORT_SYMBOL(ebt_register_target);
+EXPORT_SYMBOL(ebt_unregister_target);
+EXPORT_SYMBOL(ebt_do_table);
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux/net/core/dev.c b/kernel/linux/net/core/dev.c
new file mode 100644
index 0000000..0559f5e
--- /dev/null
+++ b/kernel/linux/net/core/dev.c
@@ -0,0 +1,2853 @@
+/*
+ * 	NET3	Protocol independent device support routines.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Derived from the non IP parts of dev.c 1.0.19
+ * 		Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *				Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ *	Additional Authors:
+ *		Florian la Roche <rzsfl@rz.uni-sb.de>
+ *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *		David Hinds <dhinds@allegro.stanford.edu>
+ *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *		Adam Sulmicki <adam@cfar.umd.edu>
+ *              Pekka Riikonen <priikone@poesidon.pspt.fi>
+ *
+ *	Changes:
+ *              D.J. Barrow     :       Fixed bug where dev->refcnt gets set to 2
+ *                                      if register_netdev gets called before
+ *                                      net_dev_init & also removed a few lines
+ *                                      of code in the process.
+ *		Alan Cox	:	device private ioctl copies fields back.
+ *		Alan Cox	:	Transmit queue code does relevant stunts to
+ *					keep the queue safe.
+ *		Alan Cox	:	Fixed double lock.
+ *		Alan Cox	:	Fixed promisc NULL pointer trap
+ *		????????	:	Support the full private ioctl range
+ *		Alan Cox	:	Moved ioctl permission check into drivers
+ *		Tim Kordas	:	SIOCADDMULTI/SIOCDELMULTI
+ *		Alan Cox	:	100 backlog just doesn't cut it when
+ *					you start doing multicast video 8)
+ *		Alan Cox	:	Rewrote net_bh and list manager.
+ *		Alan Cox	: 	Fix ETH_P_ALL echoback lengths.
+ *		Alan Cox	:	Took out transmit every packet pass
+ *					Saved a few bytes in the ioctl handler
+ *		Alan Cox	:	Network driver sets packet type before calling netif_rx. Saves
+ *					a function call a packet.
+ *		Alan Cox	:	Hashed net_bh()
+ *		Richard Kooijman:	Timestamp fixes.
+ *		Alan Cox	:	Wrong field in SIOCGIFDSTADDR
+ *		Alan Cox	:	Device lock protection.
+ *		Alan Cox	: 	Fixed nasty side effect of device close changes.
+ *		Rudi Cilibrasi	:	Pass the right thing to set_mac_address()
+ *		Dave Miller	:	32bit quantity for the device lock to make it work out
+ *					on a Sparc.
+ *		Bjorn Ekwall	:	Added KERNELD hack.
+ *		Alan Cox	:	Cleaned up the backlog initialise.
+ *		Craig Metz	:	SIOCGIFCONF fix if space for under
+ *					1 device.
+ *	    Thomas Bogendoerfer :	Return ENODEV for dev_open, if there
+ *					is no device open function.
+ *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
+ *	    Michael Chastain	:	Fix signed/unsigned for SIOCGIFCONF
+ *		Cyrus Durgin	:	Cleaned for KMOD
+ *		Adam Sulmicki   :	Bug Fix : Network Device Unload
+ *					A network device unload needs to purge
+ *					the backlog queue.
+ *	Paul Rusty Russell	:	SIOCSIFNAME
+ *              Pekka Riikonen  :	Netdev boot-time settings code
+ *              Andrew Morton   :       Make unregister_netdevice wait indefinitely on dev->refcnt
+ * 		J Hadi Salim	:	- Backlog queue sampling
+ *				        - netif_rx() feedback	
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <linux/skbuff.h>
+#include <linux/brlock.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/if_bridge.h>
+#include <linux/divert.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <net/profile.h>
+#include <net/checksum.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
+#include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+#include <net/iw_handler.h>
+#endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
+
+
+/* This define, if set, will randomly drop a packet when congestion
+ * is more than moderate.  It helps fairness in the multi-interface
+ * case when one of them is a hog, but it kills performance for the
+ * single interface case so it is off now by default.
+ */
+#undef RAND_LIE
+
+/* Setting this will sample the queue lengths and thus congestion
+ * via a timer instead of as each packet is received.
+ */
+#undef OFFLINE_SAMPLE
+
+NET_PROFILE_DEFINE(dev_queue_xmit)
+NET_PROFILE_DEFINE(softnet_process)
+
+const char *if_port_text[] = {
+  "unknown",
+  "BNC",
+  "10baseT",
+  "AUI",
+  "100baseT",
+  "100baseTX",
+  "100baseFX"
+};
+
+/*
+ *	The list of packet types we will receive (as opposed to discard)
+ *	and the routines to invoke.
+ *
+ *	Why 16. Because with 16 the only overlap we get on a hash of the
+ *	low nibble of the protocol value is RARP/SNAP/X.25.
+ *
+ *      NOTE:  That is no longer true with the addition of VLAN tags.  Not
+ *             sure which should go first, but I bet it won't make much
+ *             difference if we are running VLANs.  The good news is that
+ *             this protocol won't be in the list unless compiled in, so
+ *             the average user (w/out VLANs) will not be adversly affected.
+ *             --BLG
+ *
+ *		0800	IP
+ *		8100    802.1Q VLAN
+ *		0001	802.3
+ *		0002	AX.25
+ *		0004	802.2
+ *		8035	RARP
+ *		0005	SNAP
+ *		0805	X.25
+ *		0806	ARP
+ *		8137	IPX
+ *		0009	Localtalk
+ *		86DD	IPv6
+ */
+
+static struct packet_type *ptype_base[16];		/* 16 way hashed list */
+static struct packet_type *ptype_all = NULL;		/* Taps */
+
+#ifdef OFFLINE_SAMPLE
+static void sample_queue(unsigned long dummy);
+static struct timer_list samp_timer = { function: sample_queue };
+#endif
+
+#ifdef CONFIG_HOTPLUG
+static int net_run_sbin_hotplug(struct net_device *dev, char *action);
+#else
+#define net_run_sbin_hotplug(dev, action) ({ 0; })
+#endif
+
+/*
+ *	Our notifier list
+ */
+ 
+static struct notifier_block *netdev_chain=NULL;
+
+/*
+ *	Device drivers call our routines to queue packets here. We empty the
+ *	queue in the local softnet handler.
+ */
+struct softnet_data softnet_data[NR_CPUS] __cacheline_aligned;
+
+#ifdef CONFIG_NET_FASTROUTE
+int netdev_fastroute;
+int netdev_fastroute_obstacles;
+#endif
+
+
+/******************************************************************************************
+
+		Protocol management and registration routines
+
+*******************************************************************************************/
+
+/*
+ *	For efficiency
+ */
+
+int netdev_nit=0;
+
+/*
+ *	Add a protocol ID to the list. Now that the input handler is
+ *	smarter we can dispense with all the messy stuff that used to be
+ *	here.
+ *
+ *	BEWARE!!! Protocol handlers, mangling input packets,
+ *	MUST BE last in hash buckets and checking protocol handlers
+ *	MUST start from promiscous ptype_all chain in net_bh.
+ *	It is true now, do not change it.
+ *	Explantion follows: if protocol handler, mangling packet, will
+ *	be the first on list, it is not able to sense, that packet
+ *	is cloned and should be copied-on-write, so that it will
+ *	change it and subsequent readers will get broken packet.
+ *							--ANK (980803)
+ */
+
+/**
+ *	dev_add_pack - add packet handler
+ *	@pt: packet type declaration
+ * 
+ *	Add a protocol handler to the networking stack. The passed &packet_type
+ *	is linked into kernel lists and may not be freed until it has been
+ *	removed from the kernel lists.
+ */
+ 
+void dev_add_pack(struct packet_type *pt)
+{
+	int hash;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+
+#ifdef CONFIG_NET_FASTROUTE
+	/* Hack to detect packet socket */
+	if ((pt->data) && ((int)(pt->data)!=1)) {
+		netdev_fastroute_obstacles++;
+		dev_clear_fastroute(pt->dev);
+	}
+#endif
+	if (pt->type == htons(ETH_P_ALL)) {
+		netdev_nit++;
+		pt->next=ptype_all;
+		ptype_all=pt;
+	} else {
+		hash=ntohs(pt->type)&15;
+		pt->next = ptype_base[hash];
+		ptype_base[hash] = pt;
+	}
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+
+/**
+ *	dev_remove_pack	 - remove packet handler
+ *	@pt: packet type declaration
+ * 
+ *	Remove a protocol handler that was previously added to the kernel
+ *	protocol handlers by dev_add_pack(). The passed &packet_type is removed
+ *	from the kernel lists and can be freed or reused once this function
+ *	returns.
+ */
+ 
+void dev_remove_pack(struct packet_type *pt)
+{
+	struct packet_type **pt1;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+
+	if (pt->type == htons(ETH_P_ALL)) {
+		netdev_nit--;
+		pt1=&ptype_all;
+	} else {
+		pt1=&ptype_base[ntohs(pt->type)&15];
+	}
+
+	for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) {
+		if (pt == (*pt1)) {
+			*pt1 = pt->next;
+#ifdef CONFIG_NET_FASTROUTE
+			if (pt->data)
+				netdev_fastroute_obstacles--;
+#endif
+			br_write_unlock_bh(BR_NETPROTO_LOCK);
+			return;
+		}
+	}
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+	printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
+}
+
+/******************************************************************************
+
+		      Device Boot-time Settings Routines
+
+*******************************************************************************/
+
+/* Boot time configuration table */
+static struct netdev_boot_setup dev_boot_setup[NETDEV_BOOT_SETUP_MAX];
+
+/**
+ *	netdev_boot_setup_add	- add new setup entry
+ *	@name: name of the device
+ *	@map: configured settings for the device
+ *
+ *	Adds new setup entry to the dev_boot_setup list.  The function
+ *	returns 0 on error and 1 on success.  This is a generic routine to
+ *	all netdevices.
+ */
+int netdev_boot_setup_add(char *name, struct ifmap *map)
+{
+	struct netdev_boot_setup *s;
+	int i;
+
+	s = dev_boot_setup;
+	for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
+		if (s[i].name[0] == '\0' || s[i].name[0] == ' ') {
+			memset(s[i].name, 0, sizeof(s[i].name));
+			strcpy(s[i].name, name);
+			memcpy(&s[i].map, map, sizeof(s[i].map));
+			break;
+		}
+	}
+
+	if (i >= NETDEV_BOOT_SETUP_MAX)
+		return 0;
+
+	return 1;
+}
+
+/**
+ *	netdev_boot_setup_check	- check boot time settings
+ *	@dev: the netdevice
+ *
+ * 	Check boot time settings for the device.
+ *	The found settings are set for the device to be used
+ *	later in the device probing.
+ *	Returns 0 if no settings found, 1 if they are.
+ */
+int netdev_boot_setup_check(struct net_device *dev)
+{
+	struct netdev_boot_setup *s;
+	int i;
+
+	s = dev_boot_setup;
+	for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
+		if (s[i].name[0] != '\0' && s[i].name[0] != ' ' &&
+		    !strncmp(dev->name, s[i].name, strlen(s[i].name))) {
+			dev->irq 	= s[i].map.irq;
+			dev->base_addr 	= s[i].map.base_addr;
+			dev->mem_start 	= s[i].map.mem_start;
+			dev->mem_end 	= s[i].map.mem_end;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Saves at boot time configured settings for any netdevice.
+ */
+int __init netdev_boot_setup(char *str)
+{
+	int ints[5];
+	struct ifmap map;
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	if (!str || !*str)
+		return 0;
+
+	/* Save settings */
+	memset(&map, 0, sizeof(map));
+	if (ints[0] > 0)
+		map.irq = ints[1];
+	if (ints[0] > 1)
+		map.base_addr = ints[2];
+	if (ints[0] > 2)
+		map.mem_start = ints[3];
+	if (ints[0] > 3)
+		map.mem_end = ints[4];
+
+	/* Add new entry to the list */	
+	return netdev_boot_setup_add(str, &map);
+}
+
+__setup("netdev=", netdev_boot_setup);
+
+/*****************************************************************************************
+
+			    Device Interface Subroutines
+
+******************************************************************************************/
+
+/**
+ *	__dev_get_by_name	- find a device by its name 
+ *	@name: name to find
+ *
+ *	Find an interface by name. Must be called under RTNL semaphore
+ *	or @dev_base_lock. If the name is found a pointer to the device
+ *	is returned. If the name is not found then %NULL is returned. The
+ *	reference counters are not incremented so the caller must be
+ *	careful with locks.
+ */
+ 
+
+struct net_device *__dev_get_by_name(const char *name)
+{
+	struct net_device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (strncmp(dev->name, name, IFNAMSIZ) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+/**
+ *	dev_get_by_name		- find a device by its name
+ *	@name: name to find
+ *
+ *	Find an interface by name. This can be called from any 
+ *	context and does its own locking. The returned handle has
+ *	the usage count incremented and the caller must use dev_put() to
+ *	release it when it is no longer needed. %NULL is returned if no
+ *	matching device is found.
+ */
+
+struct net_device *dev_get_by_name(const char *name)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_name(name);
+	if (dev)
+		dev_hold(dev);
+	read_unlock(&dev_base_lock);
+	return dev;
+}
+
+/* 
+   Return value is changed to int to prevent illegal usage in future.
+   It is still legal to use to check for device existence.
+
+   User should understand, that the result returned by this function
+   is meaningless, if it was not issued under rtnl semaphore.
+ */
+
+/**
+ *	dev_get	-	test if a device exists
+ *	@name:	name to test for
+ *
+ *	Test if a name exists. Returns true if the name is found. In order
+ *	to be sure the name is not allocated or removed during the test the
+ *	caller must hold the rtnl semaphore.
+ *
+ *	This function primarily exists for back compatibility with older
+ *	drivers. 
+ */
+ 
+int dev_get(const char *name)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_name(name);
+	read_unlock(&dev_base_lock);
+	return dev != NULL;
+}
+
+/**
+ *	__dev_get_by_index - find a device by its ifindex
+ *	@ifindex: index of device
+ *
+ *	Search for an interface by index. Returns %NULL if the device
+ *	is not found or a pointer to the device. The device has not
+ *	had its reference counter increased so the caller must be careful
+ *	about locking. The caller must hold either the RTNL semaphore
+ *	or @dev_base_lock.
+ */
+
+struct net_device * __dev_get_by_index(int ifindex)
+{
+	struct net_device *dev;
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev->ifindex == ifindex)
+			return dev;
+	}
+	return NULL;
+}
+
+
+/**
+ *	dev_get_by_index - find a device by its ifindex
+ *	@ifindex: index of device
+ *
+ *	Search for an interface by index. Returns NULL if the device
+ *	is not found or a pointer to the device. The device returned has 
+ *	had a reference added and the pointer is safe until the user calls
+ *	dev_put to indicate they have finished with it.
+ */
+
+struct net_device * dev_get_by_index(int ifindex)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_index(ifindex);
+	if (dev)
+		dev_hold(dev);
+	read_unlock(&dev_base_lock);
+	return dev;
+}
+
+/**
+ *	dev_getbyhwaddr - find a device by its hardware address
+ *	@type: media type of device
+ *	@ha: hardware address
+ *
+ *	Search for an interface by MAC address. Returns NULL if the device
+ *	is not found or a pointer to the device. The caller must hold the
+ *	rtnl semaphore. The returned device has not had its ref count increased
+ *	and the caller must therefore be careful about locking
+ *
+ *	BUGS:
+ *	If the API was consistent this would be __dev_get_by_hwaddr
+ */
+
+struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
+{
+	struct net_device *dev;
+
+	ASSERT_RTNL();
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev->type == type &&
+		    memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+/**
+ *	dev_alloc_name - allocate a name for a device
+ *	@dev: device 
+ *	@name: name format string
+ *
+ *	Passed a format string - eg "lt%d" it will try and find a suitable
+ *	id. Not efficient for many devices, not called a lot. The caller
+ *	must hold the dev_base or rtnl lock while allocating the name and
+ *	adding the device in order to avoid duplicates. Returns the number
+ *	of the unit assigned or a negative errno code.
+ */
+
+int dev_alloc_name(struct net_device *dev, const char *name)
+{
+	int i;
+	char buf[32];
+	char *p;
+
+	/*
+	 * Verify the string as this thing may have come from
+	 * the user.  There must be either one "%d" and no other "%"
+	 * characters, or no "%" characters at all.
+	 */
+	p = strchr(name, '%');
+	if (p && (p[1] != 'd' || strchr(p+2, '%')))
+		return -EINVAL;
+
+	/*
+	 * If you need over 100 please also fix the algorithm...
+	 */
+	for (i = 0; i < 100; i++) {
+		snprintf(buf,sizeof(buf),name,i);
+		if (__dev_get_by_name(buf) == NULL) {
+			strcpy(dev->name, buf);
+			return i;
+		}
+	}
+	return -ENFILE;	/* Over 100 of the things .. bail out! */
+}
+
+/**
+ *	dev_alloc - allocate a network device and name
+ *	@name: name format string
+ *	@err: error return pointer
+ *
+ *	Passed a format string, eg. "lt%d", it will allocate a network device
+ *	and space for the name. %NULL is returned if no memory is available.
+ *	If the allocation succeeds then the name is assigned and the 
+ *	device pointer returned. %NULL is returned if the name allocation
+ *	failed. The cause of an error is returned as a negative errno code
+ *	in the variable @err points to.
+ *
+ *	The caller must hold the @dev_base or RTNL locks when doing this in
+ *	order to avoid duplicate name allocations.
+ */
+
+struct net_device *dev_alloc(const char *name, int *err)
+{
+	struct net_device *dev=kmalloc(sizeof(struct net_device), GFP_KERNEL);
+	if (dev == NULL) {
+		*err = -ENOBUFS;
+		return NULL;
+	}
+	memset(dev, 0, sizeof(struct net_device));
+	*err = dev_alloc_name(dev, name);
+	if (*err < 0) {
+		kfree(dev);
+		return NULL;
+	}
+	return dev;
+}
+
+/**
+ *	netdev_state_change - device changes state
+ *	@dev: device to cause notification
+ *
+ *	Called to indicate a device has changed state. This function calls
+ *	the notifier chains for netdev_chain and sends a NEWLINK message
+ *	to the routing socket.
+ */
+ 
+void netdev_state_change(struct net_device *dev)
+{
+	if (dev->flags&IFF_UP) {
+		notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+		rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
+	}
+}
+
+
+#ifdef CONFIG_KMOD
+
+/**
+ *	dev_load 	- load a network module
+ *	@name: name of interface
+ *
+ *	If a network interface is not present and the process has suitable
+ *	privileges this function loads the module. If module loading is not
+ *	available in this kernel then it becomes a nop.
+ */
+
+void dev_load(const char *name)
+{
+	if (!dev_get(name) && capable(CAP_SYS_MODULE))
+		request_module(name);
+}
+
+#else
+
+extern inline void dev_load(const char *unused){;}
+
+#endif
+
+static int default_rebuild_header(struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n", skb->dev ? skb->dev->name : "NULL!!!");
+	kfree_skb(skb);
+	return 1;
+}
+
+/**
+ *	dev_open	- prepare an interface for use. 
+ *	@dev:	device to open
+ *
+ *	Takes a device from down to up state. The device's private open
+ *	function is invoked and then the multicast lists are loaded. Finally
+ *	the device is moved into the up state and a %NETDEV_UP message is
+ *	sent to the netdev notifier chain.
+ *
+ *	Calling this function on an active interface is a nop. On a failure
+ *	a negative errno code is returned.
+ */
+ 
+int dev_open(struct net_device *dev)
+{
+	int ret = 0;
+
+	/*
+	 *	Is it already up?
+	 */
+
+	if (dev->flags&IFF_UP)
+		return 0;
+
+	/*
+	 *	Is it even present?
+	 */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/*
+	 *	Call device private open method
+	 */
+	if (try_inc_mod_count(dev->owner)) {
+		if (dev->open) {
+			ret = dev->open(dev);
+			if (ret != 0 && dev->owner)
+				__MOD_DEC_USE_COUNT(dev->owner);
+		}
+	} else {
+		ret = -ENODEV;
+	}
+
+	/*
+	 *	If it went open OK then:
+	 */
+	 
+	if (ret == 0) 
+	{
+		/*
+		 *	Set the flags.
+		 */
+		dev->flags |= IFF_UP;
+
+		set_bit(__LINK_STATE_START, &dev->state);
+
+		/*
+		 *	Initialize multicasting status 
+		 */
+		dev_mc_upload(dev);
+
+		/*
+		 *	Wakeup transmit queue engine
+		 */
+		dev_activate(dev);
+
+		/*
+		 *	... and announce new interface.
+		 */
+		notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+	}
+	return(ret);
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+
+static void dev_do_clear_fastroute(struct net_device *dev)
+{
+	if (dev->accept_fastpath) {
+		int i;
+
+		for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++) {
+			struct dst_entry *dst;
+
+			write_lock_irq(&dev->fastpath_lock);
+			dst = dev->fastpath[i];
+			dev->fastpath[i] = NULL;
+			write_unlock_irq(&dev->fastpath_lock);
+
+			dst_release(dst);
+		}
+	}
+}
+
+void dev_clear_fastroute(struct net_device *dev)
+{
+	if (dev) {
+		dev_do_clear_fastroute(dev);
+	} else {
+		read_lock(&dev_base_lock);
+		for (dev = dev_base; dev; dev = dev->next)
+			dev_do_clear_fastroute(dev);
+		read_unlock(&dev_base_lock);
+	}
+}
+#endif
+
+/**
+ *	dev_close - shutdown an interface.
+ *	@dev: device to shutdown
+ *
+ *	This function moves an active device into down state. A 
+ *	%NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
+ *	is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
+ *	chain.
+ */
+ 
+int dev_close(struct net_device *dev)
+{
+	if (!(dev->flags&IFF_UP))
+		return 0;
+
+	/*
+	 *	Tell people we are going down, so that they can
+	 *	prepare to death, when device is still operating.
+	 */
+	notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
+
+	dev_deactivate(dev);
+
+	clear_bit(__LINK_STATE_START, &dev->state);
+
+	/* Synchronize to scheduled poll. We cannot touch poll list,
+	 * it can be even on different cpu. So just clear netif_running(),
+	 * and wait when poll really will happen. Actually, the best place
+	 * for this is inside dev->stop() after device stopped its irq
+	 * engine, but this requires more changes in devices. */
+
+	smp_mb__after_clear_bit(); /* Commit netif_running(). */
+	while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) {
+		/* No hurry. */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+
+	/*
+	 *	Call the device specific close. This cannot fail.
+	 *	Only if device is UP
+	 *
+	 *	We allow it to be called even after a DETACH hot-plug
+	 *	event.
+	 */
+	 
+	if (dev->stop)
+		dev->stop(dev);
+
+	/*
+	 *	Device is now down.
+	 */
+
+	dev->flags &= ~IFF_UP;
+#ifdef CONFIG_NET_FASTROUTE
+	dev_clear_fastroute(dev);
+#endif
+
+	/*
+	 *	Tell people we are down
+	 */
+	notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+
+	/*
+	 * Drop the module refcount
+	 */
+	if (dev->owner)
+		__MOD_DEC_USE_COUNT(dev->owner);
+
+	return(0);
+}
+
+
+/*
+ *	Device change register/unregister. These are not inline or static
+ *	as we export them to the world.
+ */
+ 
+/**
+ *	register_netdevice_notifier - register a network notifier block
+ *	@nb: notifier
+ *
+ *	Register a notifier to be called when network device events occur.
+ *	The notifier passed is linked into the kernel structures and must
+ *	not be reused until it has been unregistered. A negative errno code
+ *	is returned on a failure.
+ */
+
+int register_netdevice_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_register(&netdev_chain, nb);
+}
+
+/**
+ *	unregister_netdevice_notifier - unregister a network notifier block
+ *	@nb: notifier
+ *
+ *	Unregister a notifier previously registered by
+ *	register_netdevice_notifier(). The notifier is unlinked into the
+ *	kernel structures and may then be reused. A negative errno code
+ *	is returned on a failure.
+ */
+
+int unregister_netdevice_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_unregister(&netdev_chain,nb);
+}
+
+/*
+ *	Support routine. Sends outgoing frames to any network
+ *	taps currently in use.
+ */
+
+void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct packet_type *ptype;
+	do_gettimeofday(&skb->stamp);
+
+	br_read_lock(BR_NETPROTO_LOCK);
+	for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
+	{
+		/* Never send packets back to the socket
+		 * they originated from - MvS (miquels@drinkel.ow.org)
+		 */
+		if ((ptype->dev == dev || !ptype->dev) &&
+			((struct sock *)ptype->data != skb->sk))
+		{
+			struct sk_buff *skb2;
+			if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+				break;
+
+			/* skb->nh should be correctly
+			   set by sender, so that the second statement is
+			   just protection against buggy protocols.
+			 */
+			skb2->mac.raw = skb2->data;
+
+			if (skb2->nh.raw < skb2->data || skb2->nh.raw > skb2->tail) {
+				if (net_ratelimit())
+					printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name);
+				skb2->nh.raw = skb2->data;
+			}
+
+			skb2->h.raw = skb2->nh.raw;
+			skb2->pkt_type = PACKET_OUTGOING;
+			ptype->func(skb2, skb->dev, ptype);
+		}
+	}
+	br_read_unlock(BR_NETPROTO_LOCK);
+}
+
+/* Calculate csum in the case, when packet is misrouted.
+ * If it failed by some reason, ignore and send skb with wrong
+ * checksum.
+ */
+struct sk_buff * skb_checksum_help(struct sk_buff *skb)
+{
+	int offset;
+	unsigned int csum;
+
+	offset = skb->h.raw - skb->data;
+	if (offset > (int)skb->len)
+		BUG();
+	csum = skb_checksum(skb, offset, skb->len-offset, 0);
+
+	offset = skb->tail - skb->h.raw;
+	if (offset <= 0)
+		BUG();
+	if (skb->csum+2 > offset)
+		BUG();
+
+	*(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
+	skb->ip_summed = CHECKSUM_NONE;
+	return skb;
+}
+
+#ifdef CONFIG_HIGHMEM
+/* Actually, we should eliminate this check as soon as we know, that:
+ * 1. IOMMU is present and allows to map all the memory.
+ * 2. No high memory really exists on this machine.
+ */
+
+static inline int
+illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+{
+	int i;
+
+	if (dev->features&NETIF_F_HIGHDMA)
+		return 0;
+
+	for (i=0; i<skb_shinfo(skb)->nr_frags; i++)
+		if (skb_shinfo(skb)->frags[i].page >= highmem_start_page)
+			return 1;
+
+	return 0;
+}
+#else
+#define illegal_highdma(dev, skb)	(0)
+#endif
+
+/**
+ *	dev_queue_xmit - transmit a buffer
+ *	@skb: buffer to transmit
+ *	
+ *	Queue a buffer for transmission to a network device. The caller must
+ *	have set the device and priority and built the buffer before calling this 
+ *	function. The function can be called from an interrupt.
+ *
+ *	A negative errno code is returned on a failure. A success does not
+ *	guarantee the frame will be transmitted as it may be dropped due
+ *	to congestion or traffic shaping.
+ */
+
+int dev_queue_xmit(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	struct Qdisc  *q;
+
+	if (skb_shinfo(skb)->frag_list &&
+	    !(dev->features&NETIF_F_FRAGLIST) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	/* Fragmented skb is linearized if device does not support SG,
+	 * or if at least one of fragments is in highmem and device
+	 * does not support DMA from it.
+	 */
+	if (skb_shinfo(skb)->nr_frags &&
+	    (!(dev->features&NETIF_F_SG) || illegal_highdma(dev, skb)) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+
+	/* If packet is not checksummed and device does not support
+	 * checksumming for this protocol, complete checksumming here.
+	 */
+	if (skb->ip_summed == CHECKSUM_HW &&
+	    (!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM)) &&
+	     (!(dev->features&NETIF_F_IP_CSUM) ||
+	      skb->protocol != htons(ETH_P_IP)))) {
+		if ((skb = skb_checksum_help(skb)) == NULL)
+			return -ENOMEM;
+	}
+
+	/* Grab device queue */
+	spin_lock_bh(&dev->queue_lock);
+	q = dev->qdisc;
+	if (q->enqueue) {
+		int ret = q->enqueue(skb, q);
+
+		qdisc_run(dev);
+
+		spin_unlock_bh(&dev->queue_lock);
+		return ret == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : ret;
+	}
+
+	/* The device has no queue. Common case for software devices:
+	   loopback, all the sorts of tunnels...
+
+	   Really, it is unlikely that xmit_lock protection is necessary here.
+	   (f.e. loopback and IP tunnels are clean ignoring statistics counters.)
+	   However, it is possible, that they rely on protection
+	   made by us here.
+
+	   Check this and shot the lock. It is not prone from deadlocks.
+	   Either shot noqueue qdisc, it is even simpler 8)
+	 */
+	if (dev->flags&IFF_UP) {
+		int cpu = smp_processor_id();
+
+		if (dev->xmit_lock_owner != cpu) {
+			spin_unlock(&dev->queue_lock);
+			spin_lock(&dev->xmit_lock);
+			dev->xmit_lock_owner = cpu;
+
+			if (!netif_queue_stopped(dev)) {
+				if (netdev_nit)
+					dev_queue_xmit_nit(skb,dev);
+
+				if (dev->hard_start_xmit(skb, dev) == 0) {
+					dev->xmit_lock_owner = -1;
+					spin_unlock_bh(&dev->xmit_lock);
+					return 0;
+				}
+			}
+			dev->xmit_lock_owner = -1;
+			spin_unlock_bh(&dev->xmit_lock);
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
+			kfree_skb(skb);
+			return -ENETDOWN;
+		} else {
+			/* Recursion is detected! It is possible, unfortunately */
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Dead loop on virtual device %s, fix it urgently!\n", dev->name);
+		}
+	}
+	spin_unlock_bh(&dev->queue_lock);
+
+	kfree_skb(skb);
+	return -ENETDOWN;
+}
+
+
+/*=======================================================================
+			Receiver routines
+  =======================================================================*/
+
+int netdev_max_backlog = 300;
+int weight_p = 64;            /* old backlog weight */
+/* These numbers are selected based on intuition and some
+ * experimentatiom, if you have more scientific way of doing this
+ * please go ahead and fix things.
+ */
+int no_cong_thresh = 10;
+int no_cong = 20;
+int lo_cong = 100;
+int mod_cong = 290;
+
+struct netif_rx_stats netdev_rx_stat[NR_CPUS];
+
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+atomic_t netdev_dropping = ATOMIC_INIT(0);
+static unsigned long netdev_fc_mask = 1;
+unsigned long netdev_fc_xoff = 0;
+spinlock_t netdev_fc_lock = SPIN_LOCK_UNLOCKED;
+
+static struct
+{
+	void (*stimul)(struct net_device *);
+	struct net_device *dev;
+} netdev_fc_slots[BITS_PER_LONG];
+
+int netdev_register_fc(struct net_device *dev, void (*stimul)(struct net_device *dev))
+{
+	int bit = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&netdev_fc_lock, flags);
+	if (netdev_fc_mask != ~0UL) {
+		bit = ffz(netdev_fc_mask);
+		netdev_fc_slots[bit].stimul = stimul;
+		netdev_fc_slots[bit].dev = dev;
+		set_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	spin_unlock_irqrestore(&netdev_fc_lock, flags);
+	return bit;
+}
+
+void netdev_unregister_fc(int bit)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&netdev_fc_lock, flags);
+	if (bit > 0) {
+		netdev_fc_slots[bit].stimul = NULL;
+		netdev_fc_slots[bit].dev = NULL;
+		clear_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	spin_unlock_irqrestore(&netdev_fc_lock, flags);
+}
+
+static void netdev_wakeup(void)
+{
+	unsigned long xoff;
+
+	spin_lock(&netdev_fc_lock);
+	xoff = netdev_fc_xoff;
+	netdev_fc_xoff = 0;
+	while (xoff) {
+		int i = ffz(~xoff);
+		xoff &= ~(1<<i);
+		netdev_fc_slots[i].stimul(netdev_fc_slots[i].dev);
+	}
+	spin_unlock(&netdev_fc_lock);
+}
+#endif
+
+static void get_sample_stats(int cpu)
+{
+#ifdef RAND_LIE
+	unsigned long rd;
+	int rq;
+#endif
+	int blog = softnet_data[cpu].input_pkt_queue.qlen;
+	int avg_blog = softnet_data[cpu].avg_blog;
+
+	avg_blog = (avg_blog >> 1)+ (blog >> 1);
+
+	if (avg_blog > mod_cong) {
+		/* Above moderate congestion levels. */
+		softnet_data[cpu].cng_level = NET_RX_CN_HIGH;
+#ifdef RAND_LIE
+		rd = net_random();
+		rq = rd % netdev_max_backlog;
+		if (rq < avg_blog) /* unlucky bastard */
+			softnet_data[cpu].cng_level = NET_RX_DROP;
+#endif
+	} else if (avg_blog > lo_cong) {
+		softnet_data[cpu].cng_level = NET_RX_CN_MOD;
+#ifdef RAND_LIE
+		rd = net_random();
+		rq = rd % netdev_max_backlog;
+			if (rq < avg_blog) /* unlucky bastard */
+				softnet_data[cpu].cng_level = NET_RX_CN_HIGH;
+#endif
+	} else if (avg_blog > no_cong) 
+		softnet_data[cpu].cng_level = NET_RX_CN_LOW;
+	else  /* no congestion */
+		softnet_data[cpu].cng_level = NET_RX_SUCCESS;
+
+	softnet_data[cpu].avg_blog = avg_blog;
+}
+
+#ifdef OFFLINE_SAMPLE
+static void sample_queue(unsigned long dummy)
+{
+/* 10 ms 0r 1ms -- i dont care -- JHS */
+	int next_tick = 1;
+	int cpu = smp_processor_id();
+
+	get_sample_stats(cpu);
+	next_tick += jiffies;
+	mod_timer(&samp_timer, next_tick);
+}
+#endif
+
+
+/**
+ *	netif_rx	-	post buffer to the network code
+ *	@skb: buffer to post
+ *
+ *	This function receives a packet from a device driver and queues it for
+ *	the upper (protocol) levels to process.  It always succeeds. The buffer
+ *	may be dropped during processing for congestion control or by the 
+ *	protocol layers.
+ *      
+ *	return values:
+ *	NET_RX_SUCCESS	(no congestion)           
+ *	NET_RX_CN_LOW     (low congestion) 
+ *	NET_RX_CN_MOD     (moderate congestion)
+ *	NET_RX_CN_HIGH    (high congestion) 
+ *	NET_RX_DROP    (packet was dropped)
+ *      
+ *      
+ */
+
+int netif_rx(struct sk_buff *skb)
+{
+	int this_cpu = smp_processor_id();
+	struct softnet_data *queue;
+	unsigned long flags;
+
+	if (skb->stamp.tv_sec == 0)
+		do_gettimeofday(&skb->stamp);
+
+	/* The code is rearranged so that the path is the most
+	   short when CPU is congested, but is still operating.
+	 */
+	queue = &softnet_data[this_cpu];
+
+	local_irq_save(flags);
+
+	netdev_rx_stat[this_cpu].total++;
+	if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
+		if (queue->input_pkt_queue.qlen) {
+			if (queue->throttle)
+				goto drop;
+
+enqueue:
+			dev_hold(skb->dev);
+			__skb_queue_tail(&queue->input_pkt_queue,skb);
+			local_irq_restore(flags);
+#ifndef OFFLINE_SAMPLE
+			get_sample_stats(this_cpu);
+#endif
+			return queue->cng_level;
+		}
+
+		if (queue->throttle) {
+			queue->throttle = 0;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+			if (atomic_dec_and_test(&netdev_dropping))
+				netdev_wakeup();
+#endif
+		}
+
+		netif_rx_schedule(&queue->blog_dev);
+		goto enqueue;
+	}
+
+	if (queue->throttle == 0) {
+		queue->throttle = 1;
+		netdev_rx_stat[this_cpu].throttled++;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		atomic_inc(&netdev_dropping);
+#endif
+	}
+
+drop:
+	netdev_rx_stat[this_cpu].dropped++;
+	local_irq_restore(flags);
+
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/* Deliver skb to an old protocol, which is not threaded well
+   or which do not understand shared skbs.
+ */
+static int deliver_to_old_ones(struct packet_type *pt, struct sk_buff *skb, int last)
+{
+	static spinlock_t net_bh_lock = SPIN_LOCK_UNLOCKED;
+	int ret = NET_RX_DROP;
+
+
+	if (!last) {
+		skb = skb_clone(skb, GFP_ATOMIC);
+		if (skb == NULL)
+			return ret;
+	}
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
+		kfree_skb(skb);
+		return ret;
+	}
+
+	/* The assumption (correct one) is that old protocols
+	   did not depened on BHs different of NET_BH and TIMER_BH.
+	 */
+
+	/* Emulate NET_BH with special spinlock */
+	spin_lock(&net_bh_lock);
+
+	/* Disable timers and wait for all timers completion */
+	tasklet_disable(bh_task_vec+TIMER_BH);
+
+	ret = pt->func(skb, skb->dev, pt);
+
+	tasklet_hi_enable(bh_task_vec+TIMER_BH);
+	spin_unlock(&net_bh_lock);
+	return ret;
+}
+
+static __inline__ void skb_bond(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+
+	if (dev->master)
+		skb->dev = dev->master;
+}
+
+static void net_tx_action(struct softirq_action *h)
+{
+	int cpu = smp_processor_id();
+
+	if (softnet_data[cpu].completion_queue) {
+		struct sk_buff *clist;
+
+		local_irq_disable();
+		clist = softnet_data[cpu].completion_queue;
+		softnet_data[cpu].completion_queue = NULL;
+		local_irq_enable();
+
+		while (clist != NULL) {
+			struct sk_buff *skb = clist;
+			clist = clist->next;
+
+			BUG_TRAP(atomic_read(&skb->users) == 0);
+			__kfree_skb(skb);
+		}
+	}
+
+	if (softnet_data[cpu].output_queue) {
+		struct net_device *head;
+
+		local_irq_disable();
+		head = softnet_data[cpu].output_queue;
+		softnet_data[cpu].output_queue = NULL;
+		local_irq_enable();
+
+		while (head != NULL) {
+			struct net_device *dev = head;
+			head = head->next_sched;
+
+			smp_mb__before_clear_bit();
+			clear_bit(__LINK_STATE_SCHED, &dev->state);
+
+			if (spin_trylock(&dev->queue_lock)) {
+				qdisc_run(dev);
+				spin_unlock(&dev->queue_lock);
+			} else {
+				netif_schedule(dev);
+			}
+		}
+	}
+}
+
+/**
+ *	net_call_rx_atomic
+ *	@fn: function to call
+ *
+ *	Make a function call that is atomic with respect to the protocol
+ *	layers.
+ */
+ 
+void net_call_rx_atomic(void (*fn)(void))
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	fn();
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+#endif
+
+static __inline__ int handle_bridge(struct sk_buff *skb,
+				     struct packet_type *pt_prev)
+{
+	int ret = NET_RX_DROP;
+
+	if (pt_prev) {
+		if (!pt_prev->data)
+			ret = deliver_to_old_ones(pt_prev, skb, 0);
+		else {
+			atomic_inc(&skb->users);
+			ret = pt_prev->func(skb, skb->dev, pt_prev);
+		}
+	}
+
+	return ret;
+}
+
+
+#ifdef CONFIG_NET_DIVERT
+static inline int handle_diverter(struct sk_buff *skb)
+{
+	/* if diversion is supported on device, then divert */
+	if (skb->dev->divert && skb->dev->divert->divert)
+		divert_frame(skb);
+	return 0;
+}
+#endif   /* CONFIG_NET_DIVERT */
+
+int netif_receive_skb(struct sk_buff *skb)
+{
+	struct packet_type *ptype, *pt_prev;
+	int ret = NET_RX_DROP;
+	unsigned short type = skb->protocol;
+
+	if (skb->stamp.tv_sec == 0)
+		do_gettimeofday(&skb->stamp);
+
+	skb_bond(skb);
+
+	netdev_rx_stat[smp_processor_id()].total++;
+
+#ifdef CONFIG_NET_FASTROUTE
+	if (skb->pkt_type == PACKET_FASTROUTE) {
+		netdev_rx_stat[smp_processor_id()].fastroute_deferred_out++;
+		return dev_queue_xmit(skb);
+	}
+#endif
+
+	skb->h.raw = skb->nh.raw = skb->data;
+
+	pt_prev = NULL;
+	for (ptype = ptype_all; ptype; ptype = ptype->next) {
+		if (!ptype->dev || ptype->dev == skb->dev) {
+			if (pt_prev) {
+				if (!pt_prev->data) {
+					ret = deliver_to_old_ones(pt_prev, skb, 0);
+				} else {
+					atomic_inc(&skb->users);
+					ret = pt_prev->func(skb, skb->dev, pt_prev);
+				}
+			}
+			pt_prev = ptype;
+		}
+	}
+
+#ifdef CONFIG_NET_DIVERT
+	if (skb->dev->divert && skb->dev->divert->divert)
+		ret = handle_diverter(skb);
+#endif /* CONFIG_NET_DIVERT */
+			
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+	if (skb->dev->br_port != NULL &&
+	    br_handle_frame_hook != NULL) {
+		int ret;
+
+		ret = handle_bridge(skb, pt_prev);
+		if (br_handle_frame_hook(skb) == 0)
+			return ret;
+		pt_prev = NULL;
+	}
+#endif
+
+	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+		if (ptype->type == type &&
+		    (!ptype->dev || ptype->dev == skb->dev)) {
+			if (pt_prev) {
+				if (!pt_prev->data) {
+					ret = deliver_to_old_ones(pt_prev, skb, 0);
+				} else {
+					atomic_inc(&skb->users);
+					ret = pt_prev->func(skb, skb->dev, pt_prev);
+				}
+			}
+			pt_prev = ptype;
+		}
+	}
+
+	if (pt_prev) {
+		if (!pt_prev->data) {
+			ret = deliver_to_old_ones(pt_prev, skb, 1);
+		} else {
+			ret = pt_prev->func(skb, skb->dev, pt_prev);
+		}
+	} else {
+		kfree_skb(skb);
+		/* Jamal, now you will not able to escape explaining
+		 * me how you were going to use this. :-)
+		 */
+		ret = NET_RX_DROP;
+	}
+
+	return ret;
+}
+
+static int process_backlog(struct net_device *blog_dev, int *budget)
+{
+	int work = 0;
+	int quota = min(blog_dev->quota, *budget);
+	int this_cpu = smp_processor_id();
+	struct softnet_data *queue = &softnet_data[this_cpu];
+	unsigned long start_time = jiffies;
+
+	for (;;) {
+		struct sk_buff *skb;
+		struct net_device *dev;
+
+		local_irq_disable();
+		skb = __skb_dequeue(&queue->input_pkt_queue);
+		if (skb == NULL)
+			goto job_done;
+		local_irq_enable();
+
+		dev = skb->dev;
+
+		netif_receive_skb(skb);
+
+		dev_put(dev);
+
+		work++;
+
+		if (work >= quota || jiffies - start_time > 1)
+			break;
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (queue->throttle && queue->input_pkt_queue.qlen < no_cong_thresh ) {
+			if (atomic_dec_and_test(&netdev_dropping)) {
+				queue->throttle = 0;
+				netdev_wakeup();
+				break;
+			}
+		}
+#endif
+	}
+
+	blog_dev->quota -= work;
+	*budget -= work;
+	return -1;
+
+job_done:
+	blog_dev->quota -= work;
+	*budget -= work;
+
+	list_del(&blog_dev->poll_list);
+	clear_bit(__LINK_STATE_RX_SCHED, &blog_dev->state);
+
+	if (queue->throttle) {
+		queue->throttle = 0;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (atomic_dec_and_test(&netdev_dropping))
+			netdev_wakeup();
+#endif
+	}
+	local_irq_enable();
+	return 0;
+}
+
+static void net_rx_action(struct softirq_action *h)
+{
+	int this_cpu = smp_processor_id();
+	struct softnet_data *queue = &softnet_data[this_cpu];
+	unsigned long start_time = jiffies;
+	int budget = netdev_max_backlog;
+
+	br_read_lock(BR_NETPROTO_LOCK);
+	local_irq_disable();
+
+	while (!list_empty(&queue->poll_list)) {
+		struct net_device *dev;
+
+		if (budget <= 0 || jiffies - start_time > 1)
+			goto softnet_break;
+
+		local_irq_enable();
+
+		dev = list_entry(queue->poll_list.next, struct net_device, poll_list);
+
+		if (dev->quota <= 0 || dev->poll(dev, &budget)) {
+			local_irq_disable();
+			list_del(&dev->poll_list);
+			list_add_tail(&dev->poll_list, &queue->poll_list);
+			if (dev->quota < 0)
+				dev->quota += dev->weight;
+			else
+				dev->quota = dev->weight;
+		} else {
+			dev_put(dev);
+			local_irq_disable();
+		}
+	}
+
+	local_irq_enable();
+	br_read_unlock(BR_NETPROTO_LOCK);
+	return;
+
+softnet_break:
+	netdev_rx_stat[this_cpu].time_squeeze++;
+	__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);
+
+	local_irq_enable();
+	br_read_unlock(BR_NETPROTO_LOCK);
+}
+
+static gifconf_func_t * gifconf_list [NPROTO];
+
+/**
+ *	register_gifconf	-	register a SIOCGIF handler
+ *	@family: Address family
+ *	@gifconf: Function handler
+ *
+ *	Register protocol dependent address dumping routines. The handler
+ *	that is passed must not be freed or reused until it has been replaced
+ *	by another handler.
+ */
+ 
+int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
+{
+	if (family>=NPROTO)
+		return -EINVAL;
+	gifconf_list[family] = gifconf;
+	return 0;
+}
+
+
+/*
+ *	Map an interface index to its name (SIOCGIFNAME)
+ */
+
+/*
+ *	We need this ioctl for efficient implementation of the
+ *	if_indextoname() function required by the IPv6 API.  Without
+ *	it, we would have to search all the interfaces to find a
+ *	match.  --pb
+ */
+
+static int dev_ifname(struct ifreq *arg)
+{
+	struct net_device *dev;
+	struct ifreq ifr;
+
+	/*
+	 *	Fetch the caller's info block. 
+	 */
+	
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_index(ifr.ifr_ifindex);
+	if (!dev) {
+		read_unlock(&dev_base_lock);
+		return -ENODEV;
+	}
+
+	strcpy(ifr.ifr_name, dev->name);
+	read_unlock(&dev_base_lock);
+
+	if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ *	Perform a SIOCGIFCONF call. This structure will change
+ *	size eventually, and there is nothing I can do about it.
+ *	Thus we will need a 'compatibility mode'.
+ */
+
+static int dev_ifconf(char *arg)
+{
+	struct ifconf ifc;
+	struct net_device *dev;
+	char *pos;
+	int len;
+	int total;
+	int i;
+
+	/*
+	 *	Fetch the caller's info block. 
+	 */
+	
+	if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
+		return -EFAULT;
+
+	pos = ifc.ifc_buf;
+	len = ifc.ifc_len;
+
+	/*
+	 *	Loop over the interfaces, and write an info block for each. 
+	 */
+
+	total = 0;
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		for (i=0; i<NPROTO; i++) {
+			if (gifconf_list[i]) {
+				int done;
+				if (pos==NULL) {
+					done = gifconf_list[i](dev, NULL, 0);
+				} else {
+					done = gifconf_list[i](dev, pos+total, len-total);
+				}
+				if (done<0) {
+					return -EFAULT;
+				}
+				total += done;
+			}
+		}
+  	}
+
+	/*
+	 *	All done.  Write the updated control block back to the caller. 
+	 */
+	ifc.ifc_len = total;
+
+	if (copy_to_user(arg, &ifc, sizeof(struct ifconf)))
+		return -EFAULT; 
+
+	/* 
+	 * 	Both BSD and Solaris return 0 here, so we do too.
+	 */
+	return 0;
+}
+
+/*
+ *	This is invoked by the /proc filesystem handler to display a device
+ *	in detail.
+ */
+
+#ifdef CONFIG_PROC_FS
+
+static int sprintf_stats(char *buffer, struct net_device *dev)
+{
+	struct net_device_stats *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+	int size;
+	
+	if (stats)
+		size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu %8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
+ 		   dev->name,
+		   stats->rx_bytes,
+		   stats->rx_packets, stats->rx_errors,
+		   stats->rx_dropped + stats->rx_missed_errors,
+		   stats->rx_fifo_errors,
+		   stats->rx_length_errors + stats->rx_over_errors
+		   + stats->rx_crc_errors + stats->rx_frame_errors,
+		   stats->rx_compressed, stats->multicast,
+		   stats->tx_bytes,
+		   stats->tx_packets, stats->tx_errors, stats->tx_dropped,
+		   stats->tx_fifo_errors, stats->collisions,
+		   stats->tx_carrier_errors + stats->tx_aborted_errors
+		   + stats->tx_window_errors + stats->tx_heartbeat_errors,
+		   stats->tx_compressed);
+	else
+		size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+	return size;
+}
+
+/*
+ *	Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface
+ *	to create /proc/net/dev
+ */
+ 
+static int dev_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	int len = 0;
+	off_t begin = 0;
+	off_t pos = 0;
+	int size;
+	struct net_device *dev;
+
+
+	size = sprintf(buffer, 
+		"Inter-|   Receive                                                |  Transmit\n"
+		" face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n");
+	
+	pos += size;
+	len += size;
+	
+
+	read_lock(&dev_base_lock);
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		size = sprintf_stats(buffer+len, dev);
+		len += size;
+		pos = begin + len;
+				
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+		if (pos > offset + length)
+			break;
+	}
+	read_unlock(&dev_base_lock);
+
+	*start = buffer + (offset - begin);	/* Start of wanted data */
+	len -= (offset - begin);		/* Start slop */
+	if (len > length)
+		len = length;			/* Ending slop */
+	if (len < 0)
+		len = 0;
+	return len;
+}
+
+static int dev_proc_stats(char *buffer, char **start, off_t offset,
+			  int length, int *eof, void *data)
+{
+	int i, lcpu;
+	int len=0;
+
+	for (lcpu=0; lcpu<smp_num_cpus; lcpu++) {
+		i = cpu_logical_map(lcpu);
+		len += sprintf(buffer+len, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+			       netdev_rx_stat[i].total,
+			       netdev_rx_stat[i].dropped,
+			       netdev_rx_stat[i].time_squeeze,
+			       netdev_rx_stat[i].throttled,
+			       netdev_rx_stat[i].fastroute_hit,
+			       netdev_rx_stat[i].fastroute_success,
+			       netdev_rx_stat[i].fastroute_defer,
+			       netdev_rx_stat[i].fastroute_deferred_out,
+#if 0
+			       netdev_rx_stat[i].fastroute_latency_reduction
+#else
+			       netdev_rx_stat[i].cpu_collision
+#endif
+			       );
+	}
+
+	len -= offset;
+
+	if (len > length)
+		len = length;
+	if (len < 0)
+		len = 0;
+
+	*start = buffer + offset;
+	*eof = 1;
+
+	return len;
+}
+
+#endif	/* CONFIG_PROC_FS */
+
+
+/**
+ *	netdev_set_master	-	set up master/slave pair
+ *	@slave: slave device
+ *	@master: new master device
+ *
+ *	Changes the master device of the slave. Pass %NULL to break the
+ *	bonding. The caller must hold the RTNL semaphore. On a failure
+ *	a negative errno code is returned. On success the reference counts
+ *	are adjusted, %RTM_NEWLINK is sent to the routing socket and the
+ *	function returns zero.
+ */
+ 
+int netdev_set_master(struct net_device *slave, struct net_device *master)
+{
+	struct net_device *old = slave->master;
+
+	ASSERT_RTNL();
+
+	if (master) {
+		if (old)
+			return -EBUSY;
+		dev_hold(master);
+	}
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	slave->master = master;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+	if (old)
+		dev_put(old);
+
+	if (master)
+		slave->flags |= IFF_SLAVE;
+	else
+		slave->flags &= ~IFF_SLAVE;
+
+	rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
+	return 0;
+}
+
+/**
+ *	dev_set_promiscuity	- update promiscuity count on a device
+ *	@dev: device
+ *	@inc: modifier
+ *
+ *	Add or remove promsicuity from a device. While the count in the device
+ *	remains above zero the interface remains promiscuous. Once it hits zero
+ *	the device reverts back to normal filtering operation. A negative inc
+ *	value is used to drop promiscuity on the device.
+ */
+ 
+void dev_set_promiscuity(struct net_device *dev, int inc)
+{
+	unsigned short old_flags = dev->flags;
+
+	dev->flags |= IFF_PROMISC;
+	if ((dev->promiscuity += inc) == 0)
+		dev->flags &= ~IFF_PROMISC;
+	if (dev->flags^old_flags) {
+#ifdef CONFIG_NET_FASTROUTE
+		if (dev->flags&IFF_PROMISC) {
+			netdev_fastroute_obstacles++;
+			dev_clear_fastroute(dev);
+		} else
+			netdev_fastroute_obstacles--;
+#endif
+		dev_mc_upload(dev);
+		printk(KERN_INFO "device %s %s promiscuous mode\n",
+		       dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "left");
+	}
+}
+
+/**
+ *	dev_set_allmulti	- update allmulti count on a device
+ *	@dev: device
+ *	@inc: modifier
+ *
+ *	Add or remove reception of all multicast frames to a device. While the
+ *	count in the device remains above zero the interface remains listening
+ *	to all interfaces. Once it hits zero the device reverts back to normal
+ *	filtering operation. A negative @inc value is used to drop the counter
+ *	when releasing a resource needing all multicasts.
+ */
+
+void dev_set_allmulti(struct net_device *dev, int inc)
+{
+	unsigned short old_flags = dev->flags;
+
+	dev->flags |= IFF_ALLMULTI;
+	if ((dev->allmulti += inc) == 0)
+		dev->flags &= ~IFF_ALLMULTI;
+	if (dev->flags^old_flags)
+		dev_mc_upload(dev);
+}
+
+int dev_change_flags(struct net_device *dev, unsigned flags)
+{
+	int ret;
+	int old_flags = dev->flags;
+
+	/*
+	 *	Set the flags on our device.
+	 */
+
+	dev->flags = (flags & (IFF_DEBUG|IFF_NOTRAILERS|IFF_NOARP|IFF_DYNAMIC|
+			       IFF_MULTICAST|IFF_PORTSEL|IFF_AUTOMEDIA)) |
+				       (dev->flags & (IFF_UP|IFF_VOLATILE|IFF_PROMISC|IFF_ALLMULTI));
+
+	/*
+	 *	Load in the correct multicast list now the flags have changed.
+	 */				
+
+	dev_mc_upload(dev);
+
+	/*
+	 *	Have we downed the interface. We handle IFF_UP ourselves
+	 *	according to user attempts to set it, rather than blindly
+	 *	setting it.
+	 */
+
+	ret = 0;
+	if ((old_flags^flags)&IFF_UP)	/* Bit is different  ? */
+	{
+		ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
+
+		if (ret == 0) 
+			dev_mc_upload(dev);
+	}
+
+	if (dev->flags&IFF_UP &&
+	    ((old_flags^dev->flags)&~(IFF_UP|IFF_PROMISC|IFF_ALLMULTI|IFF_VOLATILE)))
+		notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+
+	if ((flags^dev->gflags)&IFF_PROMISC) {
+		int inc = (flags&IFF_PROMISC) ? +1 : -1;
+		dev->gflags ^= IFF_PROMISC;
+		dev_set_promiscuity(dev, inc);
+	}
+
+	/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
+	   is important. Some (broken) drivers set IFF_PROMISC, when
+	   IFF_ALLMULTI is requested not asking us and not reporting.
+	 */
+	if ((flags^dev->gflags)&IFF_ALLMULTI) {
+		int inc = (flags&IFF_ALLMULTI) ? +1 : -1;
+		dev->gflags ^= IFF_ALLMULTI;
+		dev_set_allmulti(dev, inc);
+	}
+
+	if (old_flags^dev->flags)
+		rtmsg_ifinfo(RTM_NEWLINK, dev, old_flags^dev->flags);
+
+	return ret;
+}
+
+/*
+ *	Perform the SIOCxIFxxx calls. 
+ */
+ 
+static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
+{
+	struct net_device *dev;
+	int err;
+
+	if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL)
+		return -ENODEV;
+
+	switch(cmd) 
+	{
+		case SIOCGIFFLAGS:	/* Get interface flags */
+			ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI|IFF_RUNNING))
+				|(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI));
+			if (netif_running(dev) && netif_carrier_ok(dev))
+				ifr->ifr_flags |= IFF_RUNNING;
+			return 0;
+
+		case SIOCSIFFLAGS:	/* Set interface flags */
+			return dev_change_flags(dev, ifr->ifr_flags);
+		
+		case SIOCGIFMETRIC:	/* Get the metric on the interface (currently unused) */
+			ifr->ifr_metric = 0;
+			return 0;
+			
+		case SIOCSIFMETRIC:	/* Set the metric on the interface (currently unused) */
+			return -EOPNOTSUPP;
+	
+		case SIOCGIFMTU:	/* Get the MTU of a device */
+			ifr->ifr_mtu = dev->mtu;
+			return 0;
+	
+		case SIOCSIFMTU:	/* Set the MTU of a device */
+			if (ifr->ifr_mtu == dev->mtu)
+				return 0;
+
+			/*
+			 *	MTU must be positive.
+			 */
+			 
+			if (ifr->ifr_mtu<0)
+				return -EINVAL;
+
+			if (!netif_device_present(dev))
+				return -ENODEV;
+
+			if (dev->change_mtu)
+				err = dev->change_mtu(dev, ifr->ifr_mtu);
+			else {
+				dev->mtu = ifr->ifr_mtu;
+				err = 0;
+			}
+			if (!err && dev->flags&IFF_UP)
+				notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev);
+			return err;
+
+		case SIOCGIFHWADDR:
+			memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+			ifr->ifr_hwaddr.sa_family=dev->type;
+			return 0;
+				
+		case SIOCSIFHWADDR:
+			if (dev->set_mac_address == NULL)
+				return -EOPNOTSUPP;
+			if (ifr->ifr_hwaddr.sa_family!=dev->type)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			err = dev->set_mac_address(dev, &ifr->ifr_hwaddr);
+			if (!err)
+				notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+			return err;
+			
+		case SIOCSIFHWBROADCAST:
+			if (ifr->ifr_hwaddr.sa_family!=dev->type)
+				return -EINVAL;
+			memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN);
+			notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+			return 0;
+
+		case SIOCGIFMAP:
+			ifr->ifr_map.mem_start=dev->mem_start;
+			ifr->ifr_map.mem_end=dev->mem_end;
+			ifr->ifr_map.base_addr=dev->base_addr;
+			ifr->ifr_map.irq=dev->irq;
+			ifr->ifr_map.dma=dev->dma;
+			ifr->ifr_map.port=dev->if_port;
+			return 0;
+			
+		case SIOCSIFMAP:
+			if (dev->set_config) {
+				if (!netif_device_present(dev))
+					return -ENODEV;
+				return dev->set_config(dev,&ifr->ifr_map);
+			}
+			return -EOPNOTSUPP;
+			
+		case SIOCADDMULTI:
+			if (dev->set_multicast_list == NULL ||
+			    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1);
+			return 0;
+
+		case SIOCDELMULTI:
+			if (dev->set_multicast_list == NULL ||
+			    ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1);
+			return 0;
+
+		case SIOCGIFINDEX:
+			ifr->ifr_ifindex = dev->ifindex;
+			return 0;
+
+		case SIOCGIFTXQLEN:
+			ifr->ifr_qlen = dev->tx_queue_len;
+			return 0;
+
+		case SIOCSIFTXQLEN:
+			if (ifr->ifr_qlen<0)
+				return -EINVAL;
+			dev->tx_queue_len = ifr->ifr_qlen;
+			return 0;
+
+		case SIOCSIFNAME:
+			if (dev->flags&IFF_UP)
+				return -EBUSY;
+			if (__dev_get_by_name(ifr->ifr_newname))
+				return -EEXIST;
+			memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+			dev->name[IFNAMSIZ-1] = 0;
+			notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+			return 0;
+
+		/*
+		 *	Unknown or private ioctl
+		 */
+
+		default:
+			if ((cmd >= SIOCDEVPRIVATE &&
+			    cmd <= SIOCDEVPRIVATE + 15) ||
+			    cmd == SIOCBONDENSLAVE ||
+			    cmd == SIOCBONDRELEASE ||
+			    cmd == SIOCBONDSETHWADDR ||
+			    cmd == SIOCBONDSLAVEINFOQUERY ||
+			    cmd == SIOCBONDINFOQUERY ||
+			    cmd == SIOCBONDCHANGEACTIVE ||
+			    cmd == SIOCETHTOOL ||
+			    cmd == SIOCGMIIPHY ||
+			    cmd == SIOCGMIIREG ||
+			    cmd == SIOCSMIIREG) {
+				if (dev->do_ioctl) {
+					if (!netif_device_present(dev))
+						return -ENODEV;
+					return dev->do_ioctl(dev, ifr, cmd);
+				}
+				return -EOPNOTSUPP;
+			}
+
+	}
+	return -EINVAL;
+}
+
+/*
+ *	This function handles all "interface"-type I/O control requests. The actual
+ *	'doing' part of this is dev_ifsioc above.
+ */
+
+/**
+ *	dev_ioctl	-	network device ioctl
+ *	@cmd: command to issue
+ *	@arg: pointer to a struct ifreq in user space
+ *
+ *	Issue ioctl functions to devices. This is normally called by the
+ *	user space syscall interfaces but can sometimes be useful for 
+ *	other purposes. The return value is the return from the syscall if
+ *	positive or a negative errno code on error.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+	struct ifreq ifr;
+	int ret;
+	char *colon;
+
+	/* One special case: SIOCGIFCONF takes ifconf argument
+	   and requires shared lock, because it sleeps writing
+	   to user space.
+	 */
+	   
+	if (cmd == SIOCGIFCONF) {
+		rtnl_shlock();
+		ret = dev_ifconf((char *) arg);
+		rtnl_shunlock();
+		return ret;
+	}
+	if (cmd == SIOCGIFNAME) {
+		return dev_ifname((struct ifreq *)arg);
+	}
+
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
+
+	ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+	colon = strchr(ifr.ifr_name, ':');
+	if (colon)
+		*colon = 0;
+
+	/*
+	 *	See which interface the caller is talking about. 
+	 */
+	 
+	switch(cmd) 
+	{
+		/*
+		 *	These ioctl calls:
+		 *	- can be done by all.
+		 *	- atomic and do not require locking.
+		 *	- return a value
+		 */
+		 
+		case SIOCGIFFLAGS:
+		case SIOCGIFMETRIC:
+		case SIOCGIFMTU:
+		case SIOCGIFHWADDR:
+		case SIOCGIFSLAVE:
+		case SIOCGIFMAP:
+		case SIOCGIFINDEX:
+		case SIOCGIFTXQLEN:
+			dev_load(ifr.ifr_name);
+			read_lock(&dev_base_lock);
+			ret = dev_ifsioc(&ifr, cmd);
+			read_unlock(&dev_base_lock);
+			if (!ret) {
+				if (colon)
+					*colon = ':';
+				if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+					return -EFAULT;
+			}
+			return ret;
+
+		/*
+		 *	These ioctl calls:
+		 *	- require superuser power.
+		 *	- require strict serialization.
+		 *	- return a value
+		 */
+		 
+		case SIOCETHTOOL:
+		case SIOCGMIIPHY:
+		case SIOCGMIIREG:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			dev_load(ifr.ifr_name);
+			dev_probe_lock();
+			rtnl_lock();
+			ret = dev_ifsioc(&ifr, cmd);
+			rtnl_unlock();
+			dev_probe_unlock();
+			if (!ret) {
+				if (colon)
+					*colon = ':';
+				if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+					return -EFAULT;
+			}
+			return ret;
+
+		/*
+		 *	These ioctl calls:
+		 *	- require superuser power.
+		 *	- require strict serialization.
+		 *	- do not return a value
+		 */
+		 
+		case SIOCSIFFLAGS:
+		case SIOCSIFMETRIC:
+		case SIOCSIFMTU:
+		case SIOCSIFMAP:
+		case SIOCSIFHWADDR:
+		case SIOCSIFSLAVE:
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+		case SIOCSIFHWBROADCAST:
+		case SIOCSIFTXQLEN:
+		case SIOCSIFNAME:
+		case SIOCSMIIREG:
+		case SIOCBONDENSLAVE:
+		case SIOCBONDRELEASE:
+		case SIOCBONDSETHWADDR:
+		case SIOCBONDSLAVEINFOQUERY:
+		case SIOCBONDINFOQUERY:
+		case SIOCBONDCHANGEACTIVE:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			dev_load(ifr.ifr_name);
+			dev_probe_lock();
+			rtnl_lock();
+			ret = dev_ifsioc(&ifr, cmd);
+			rtnl_unlock();
+			dev_probe_unlock();
+			return ret;
+	
+		case SIOCGIFMEM:
+			/* Get the per device memory space. We can add this but currently
+			   do not support it */
+		case SIOCSIFMEM:
+			/* Set the per device memory buffer space. Not applicable in our case */
+		case SIOCSIFLINK:
+			return -EINVAL;
+
+		/*
+		 *	Unknown or private ioctl.
+		 */	
+		 
+		default:
+			if (cmd >= SIOCDEVPRIVATE &&
+			    cmd <= SIOCDEVPRIVATE + 15) {
+				dev_load(ifr.ifr_name);
+				dev_probe_lock();
+				rtnl_lock();
+				ret = dev_ifsioc(&ifr, cmd);
+				rtnl_unlock();
+				dev_probe_unlock();
+				if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+					return -EFAULT;
+				return ret;
+			}
+#ifdef WIRELESS_EXT
+			/* Take care of Wireless Extensions */
+			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+				/* If command is `set a parameter', or
+				 * `get the encoding parameters', check if
+				 * the user has the right to do it */
+				if (IW_IS_SET(cmd) || (cmd == SIOCGIWENCODE)) {
+					if(!capable(CAP_NET_ADMIN))
+						return -EPERM;
+				}
+				dev_load(ifr.ifr_name);
+				rtnl_lock();
+				/* Follow me in net/core/wireless.c */
+				ret = wireless_process_ioctl(&ifr, cmd);
+				rtnl_unlock();
+				if (!ret && IW_IS_GET(cmd) &&
+				    copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+					return -EFAULT;
+				return ret;
+			}
+#endif	/* WIRELESS_EXT */
+			return -EINVAL;
+	}
+}
+
+
+/**
+ *	dev_new_index	-	allocate an ifindex
+ *
+ *	Returns a suitable unique value for a new device interface
+ *	number.  The caller must hold the rtnl semaphore or the
+ *	dev_base_lock to be sure it remains unique.
+ */
+ 
+int dev_new_index(void)
+{
+	static int ifindex;
+	for (;;) {
+		if (++ifindex <= 0)
+			ifindex=1;
+		if (__dev_get_by_index(ifindex) == NULL)
+			return ifindex;
+	}
+}
+
+static int dev_boot_phase = 1;
+
+/**
+ *	register_netdevice	- register a network device
+ *	@dev: device to register
+ *	
+ *	Take a completed network device structure and add it to the kernel
+ *	interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
+ *	chain. 0 is returned on success. A negative errno code is returned
+ *	on a failure to set up the device, or if the name is a duplicate.
+ *
+ *	Callers must hold the rtnl semaphore.  See the comment at the
+ *	end of Space.c for details about the locking.  You may want
+ *	register_netdev() instead of this.
+ *
+ *	BUGS:
+ *	The locking appears insufficient to guarantee two parallel registers
+ *	will not get the same name.
+ */
+
+int net_dev_init(void);
+
+int register_netdevice(struct net_device *dev)
+{
+	struct net_device *d, **dp;
+#ifdef CONFIG_NET_DIVERT
+	int ret;
+#endif
+
+	spin_lock_init(&dev->queue_lock);
+	spin_lock_init(&dev->xmit_lock);
+	dev->xmit_lock_owner = -1;
+#ifdef CONFIG_NET_FASTROUTE
+	dev->fastpath_lock=RW_LOCK_UNLOCKED;
+#endif
+
+	if (dev_boot_phase)
+		net_dev_init();
+
+#ifdef CONFIG_NET_DIVERT
+	ret = alloc_divert_blk(dev);
+	if (ret)
+		return ret;
+#endif /* CONFIG_NET_DIVERT */
+	
+	dev->iflink = -1;
+
+	/* Init, if this function is available */
+	if (dev->init && dev->init(dev) != 0) {
+#ifdef CONFIG_NET_DIVERT
+		free_divert_blk(dev);
+#endif
+		return -EIO;
+	}
+
+	dev->ifindex = dev_new_index();
+	if (dev->iflink == -1)
+		dev->iflink = dev->ifindex;
+
+	/* Check for existence, and append to tail of chain */
+	for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
+		if (d == dev || strcmp(d->name, dev->name) == 0) {
+#ifdef CONFIG_NET_DIVERT
+			free_divert_blk(dev);
+#endif
+			return -EEXIST;
+		}
+	}
+	/*
+	 *	nil rebuild_header routine,
+	 *	that should be never called and used as just bug trap.
+	 */
+
+	if (dev->rebuild_header == NULL)
+		dev->rebuild_header = default_rebuild_header;
+
+	/*
+	 *	Default initial state at registry is that the
+	 *	device is present.
+	 */
+
+	set_bit(__LINK_STATE_PRESENT, &dev->state);
+
+	dev->next = NULL;
+	dev_init_scheduler(dev);
+	write_lock_bh(&dev_base_lock);
+	*dp = dev;
+	dev_hold(dev);
+	dev->deadbeaf = 0;
+	write_unlock_bh(&dev_base_lock);
+
+	/* Notify protocols, that a new device appeared. */
+	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
+
+	net_run_sbin_hotplug(dev, "register");
+
+	return 0;
+}
+
+/**
+ *	netdev_finish_unregister - complete unregistration
+ *	@dev: device
+ *
+ *	Destroy and free a dead device. A value of zero is returned on
+ *	success.
+ */
+ 
+int netdev_finish_unregister(struct net_device *dev)
+{
+	BUG_TRAP(dev->ip_ptr==NULL);
+	BUG_TRAP(dev->ip6_ptr==NULL);
+	BUG_TRAP(dev->dn_ptr==NULL);
+
+	if (!dev->deadbeaf) {
+		printk(KERN_ERR "Freeing alive device %p, %s\n", dev, dev->name);
+		return 0;
+	}
+#ifdef NET_REFCNT_DEBUG
+	printk(KERN_DEBUG "netdev_finish_unregister: %s%s.\n", dev->name,
+	       (dev->features & NETIF_F_DYNALLOC)?"":", old style");
+#endif
+	if (dev->destructor)
+		dev->destructor(dev);
+	if (dev->features & NETIF_F_DYNALLOC)
+		kfree(dev);
+	return 0;
+}
+
+/**
+ *	unregister_netdevice - remove device from the kernel
+ *	@dev: device
+ *
+ *	This function shuts down a device interface and removes it
+ *	from the kernel tables. On success 0 is returned, on a failure
+ *	a negative errno code is returned.
+ *
+ *	Callers must hold the rtnl semaphore.  See the comment at the
+ *	end of Space.c for details about the locking.  You may want
+ *	unregister_netdev() instead of this.
+ */
+
+int unregister_netdevice(struct net_device *dev)
+{
+	unsigned long now, warning_time;
+	struct net_device *d, **dp;
+
+	/* If device is running, close it first. */
+	if (dev->flags & IFF_UP)
+		dev_close(dev);
+
+	BUG_TRAP(dev->deadbeaf==0);
+	dev->deadbeaf = 1;
+
+	/* And unlink it from device chain. */
+	for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+		if (d == dev) {
+			write_lock_bh(&dev_base_lock);
+			*dp = d->next;
+			write_unlock_bh(&dev_base_lock);
+			break;
+		}
+	}
+	if (d == NULL) {
+		printk(KERN_DEBUG "unregister_netdevice: device %s/%p never was registered\n", dev->name, dev);
+		return -ENODEV;
+	}
+
+	/* Synchronize to net_rx_action. */
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+	if (dev_boot_phase == 0) {
+#ifdef CONFIG_NET_FASTROUTE
+		dev_clear_fastroute(dev);
+#endif
+
+		/* Shutdown queueing discipline. */
+		dev_shutdown(dev);
+
+		net_run_sbin_hotplug(dev, "unregister");
+
+		/* Notify protocols, that we are about to destroy
+		   this device. They should clean all the things.
+		 */
+		notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
+
+		/*
+		 *	Flush the multicast chain
+		 */
+		dev_mc_discard(dev);
+	}
+
+	if (dev->uninit)
+		dev->uninit(dev);
+
+	/* Notifier chain MUST detach us from master device. */
+	BUG_TRAP(dev->master==NULL);
+
+#ifdef CONFIG_NET_DIVERT
+	free_divert_blk(dev);
+#endif
+
+	if (dev->features & NETIF_F_DYNALLOC) {
+#ifdef NET_REFCNT_DEBUG
+		if (atomic_read(&dev->refcnt) != 1)
+			printk(KERN_DEBUG "unregister_netdevice: holding %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt)-1);
+#endif
+		dev_put(dev);
+		return 0;
+	}
+
+	/* Last reference is our one */
+	if (atomic_read(&dev->refcnt) == 1) {
+		dev_put(dev);
+		return 0;
+	}
+
+#ifdef NET_REFCNT_DEBUG
+	printk("unregister_netdevice: waiting %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt));
+#endif
+
+	/* EXPLANATION. If dev->refcnt is not now 1 (our own reference)
+	   it means that someone in the kernel still has a reference
+	   to this device and we cannot release it.
+
+	   "New style" devices have destructors, hence we can return from this
+	   function and destructor will do all the work later.  As of kernel 2.4.0
+	   there are very few "New Style" devices.
+
+	   "Old style" devices expect that the device is free of any references
+	   upon exit from this function.
+	   We cannot return from this function until all such references have
+	   fallen away.  This is because the caller of this function will probably
+	   immediately kfree(*dev) and then be unloaded via sys_delete_module.
+
+	   So, we linger until all references fall away.  The duration of the
+	   linger is basically unbounded! It is driven by, for example, the
+	   current setting of sysctl_ipfrag_time.
+
+	   After 1 second, we start to rebroadcast unregister notifications
+	   in hope that careless clients will release the device.
+
+	 */
+
+	now = warning_time = jiffies;
+	while (atomic_read(&dev->refcnt) != 1) {
+		if ((jiffies - now) > 1*HZ) {
+			/* Rebroadcast unregister notification */
+			notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
+		}
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ/4);
+		current->state = TASK_RUNNING;
+		if ((jiffies - warning_time) > 10*HZ) {
+			printk(KERN_EMERG "unregister_netdevice: waiting for %s to "
+					"become free. Usage count = %d\n",
+					dev->name, atomic_read(&dev->refcnt));
+			warning_time = jiffies;
+		}
+	}
+	dev_put(dev);
+	return 0;
+}
+
+
+/*
+ *	Initialize the DEV module. At boot time this walks the device list and
+ *	unhooks any devices that fail to initialise (normally hardware not 
+ *	present) and leaves us with a valid list of present and active devices.
+ *
+ */
+
+extern void net_device_init(void);
+extern void ip_auto_config(void);
+struct proc_dir_entry *proc_net_drivers;
+#ifdef CONFIG_NET_DIVERT
+extern void dv_init(void);
+#endif /* CONFIG_NET_DIVERT */
+
+
+/*
+ *       Callers must hold the rtnl semaphore.  See the comment at the
+ *       end of Space.c for details about the locking.
+ */
+int __init net_dev_init(void)
+{
+	struct net_device *dev, **dp;
+	int i;
+
+	if (!dev_boot_phase)
+		return 0;
+
+
+#ifdef CONFIG_NET_DIVERT
+	dv_init();
+#endif /* CONFIG_NET_DIVERT */
+	
+	/*
+	 *	Initialise the packet receive queues.
+	 */
+
+	for (i = 0; i < NR_CPUS; i++) {
+		struct softnet_data *queue;
+
+		queue = &softnet_data[i];
+		skb_queue_head_init(&queue->input_pkt_queue);
+		queue->throttle = 0;
+		queue->cng_level = 0;
+		queue->avg_blog = 10; /* arbitrary non-zero */
+		queue->completion_queue = NULL;
+		INIT_LIST_HEAD(&queue->poll_list);
+		set_bit(__LINK_STATE_START, &queue->blog_dev.state);
+		queue->blog_dev.weight = weight_p;
+		queue->blog_dev.poll = process_backlog;
+		atomic_set(&queue->blog_dev.refcnt, 1);
+	}
+
+#ifdef CONFIG_NET_PROFILE
+	net_profile_init();
+	NET_PROFILE_REGISTER(dev_queue_xmit);
+	NET_PROFILE_REGISTER(softnet_process);
+#endif
+
+#ifdef OFFLINE_SAMPLE
+	samp_timer.expires = jiffies + (10 * HZ);
+	add_timer(&samp_timer);
+#endif
+
+	/*
+	 *	Add the devices.
+	 *	If the call to dev->init fails, the dev is removed
+	 *	from the chain disconnecting the device until the
+	 *	next reboot.
+	 *
+	 *	NB At boot phase networking is dead. No locking is required.
+	 *	But we still preserve dev_base_lock for sanity.
+	 */
+
+	dp = &dev_base;
+	while ((dev = *dp) != NULL) {
+		spin_lock_init(&dev->queue_lock);
+		spin_lock_init(&dev->xmit_lock);
+#ifdef CONFIG_NET_FASTROUTE
+		dev->fastpath_lock = RW_LOCK_UNLOCKED;
+#endif
+		dev->xmit_lock_owner = -1;
+		dev->iflink = -1;
+		dev_hold(dev);
+
+		/*
+		 * Allocate name. If the init() fails
+		 * the name will be reissued correctly.
+		 */
+		if (strchr(dev->name, '%'))
+			dev_alloc_name(dev, dev->name);
+
+		/* 
+		 * Check boot time settings for the device.
+		 */
+		netdev_boot_setup_check(dev);
+
+		if (dev->init && dev->init(dev)) {
+			/*
+			 * It failed to come up. It will be unhooked later.
+			 * dev_alloc_name can now advance to next suitable
+			 * name that is checked next.
+			 */
+			dev->deadbeaf = 1;
+			dp = &dev->next;
+		} else {
+			dp = &dev->next;
+			dev->ifindex = dev_new_index();
+			if (dev->iflink == -1)
+				dev->iflink = dev->ifindex;
+			if (dev->rebuild_header == NULL)
+				dev->rebuild_header = default_rebuild_header;
+			dev_init_scheduler(dev);
+			set_bit(__LINK_STATE_PRESENT, &dev->state);
+		}
+	}
+
+	/*
+	 * Unhook devices that failed to come up
+	 */
+	dp = &dev_base;
+	while ((dev = *dp) != NULL) {
+		if (dev->deadbeaf) {
+			write_lock_bh(&dev_base_lock);
+			*dp = dev->next;
+			write_unlock_bh(&dev_base_lock);
+			dev_put(dev);
+		} else {
+			dp = &dev->next;
+		}
+	}
+
+#ifdef CONFIG_PROC_FS
+	proc_net_create("dev", 0, dev_get_info);
+	create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
+	proc_net_drivers = proc_mkdir("net/drivers", 0);
+#ifdef WIRELESS_EXT
+	/* Available in net/core/wireless.c */
+	proc_net_create("wireless", 0, dev_get_wireless_info);
+#endif	/* WIRELESS_EXT */
+#endif	/* CONFIG_PROC_FS */
+
+	dev_boot_phase = 0;
+
+	open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
+	open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
+
+	dst_init();
+	dev_mcast_init();
+
+#ifdef CONFIG_NET_SCHED
+	pktsched_init();
+#endif
+	/*
+	 *	Initialise network devices
+	 */
+	 
+	net_device_init();
+
+	return 0;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/* Notify userspace when a netdevice event occurs,
+ * by running '/sbin/hotplug net' with certain
+ * environment variables set.
+ */
+
+static int net_run_sbin_hotplug(struct net_device *dev, char *action)
+{
+	char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32];
+	int i;
+
+	sprintf(ifname, "INTERFACE=%s", dev->name);
+	sprintf(action_str, "ACTION=%s", action);
+
+        i = 0;
+        argv[i++] = hotplug_path;
+        argv[i++] = "net";
+        argv[i] = 0;
+
+	i = 0;
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp [i++] = ifname;
+	envp [i++] = action_str;
+	envp [i] = 0;
+	
+	return call_usermodehelper(argv [0], argv, envp);
+}
+#endif
diff --git a/kernel/linux2.5/MAINTAINERS b/kernel/linux2.5/MAINTAINERS
new file mode 100644
index 0000000..4cc52bc
--- /dev/null
+++ b/kernel/linux2.5/MAINTAINERS
@@ -0,0 +1,1976 @@
+	List of maintainers and how to submit kernel changes
+
+Please try to follow the guidelines below.  This will make things
+easier on the maintainers.  Not all of these guidelines matter for every
+trivial patch so apply some common sense.
+
+1.	Always _test_ your changes, however small, on at least 4 or
+	5 people, preferably many more.
+
+2.	Try to release a few ALPHA test versions to the net. Announce
+	them onto the kernel channel and await results. This is especially
+	important for device drivers, because often that's the only way
+	you will find things like the fact version 3 firmware needs
+	a magic fix you didn't know about, or some clown changed the
+	chips on a board and not its name.  (Don't laugh!  Look at the
+	SMC etherpower for that.)
+
+3.	Make sure your changes compile correctly in multiple
+	configurations. In particular check that changes work both as a
+	module and built into the kernel.
+
+4.	When you are happy with a change make it generally available for
+	testing and await feedback.
+
+5.	Make a patch available to the relevant maintainer in the list. Use
+	'diff -u' to make the patch easy to merge. Be prepared to get your
+	changes sent back with seemingly silly requests about formatting
+	and variable names.  These aren't as silly as they seem. One
+	job the maintainers (and especially Linus) do is to keep things
+	looking the same. Sometimes this means that the clever hack in
+	your driver to get around a problem actually needs to become a
+	generalized kernel feature ready for next time. See
+	Documentation/CodingStyle for guidance here.
+
+	PLEASE try to include any credit lines you want added with the
+	patch. It avoids people being missed off by mistake and makes
+	it easier to know who wants adding and who doesn't.
+
+	PLEASE document known bugs. If it doesn't work for everything
+	or does something very odd once a month document it.
+
+6.	Make sure you have the right to send any changes you make. If you
+	do changes at work you may find your employer owns the patch
+	not you.
+
+7.	Happy hacking.
+
+ 		-----------------------------------
+
+Maintainers List (try to look for most precise areas first)
+
+Note: For the hard of thinking, this list is meant to remain in alphabetical
+order. If you could add yourselves to it in alphabetical order that would be
+so much easier [Ed]
+
+P: Person
+M: Mail patches to
+L: Mailing list that is relevant to this area
+W: Web-page with status/info
+S: Status, one of the following:
+
+	Supported:	Someone is actually paid to look after this.
+	Maintained:	Someone actually looks after it.
+	Odd Fixes:	It has a maintainer but they don't have time to do
+			much other than throw the odd patch in. See below..
+	Orphan:		No current maintainer [but maybe you could take the
+			role as you write your new code].
+	Obsolete:	Old code. Something tagged obsolete generally means
+			it has been replaced by a better system and you
+			should be using that.
+
+3C359 NETWORK DRIVER
+P:	Mike Phillips
+M:	mikep@linuxtr.net
+L:	linux-net@vger.rutgers.edu
+L:	linux-tr@linuxtr.net
+W:	http://www.linuxtr.net
+S:	Maintained
+
+3C501 NETWORK DRIVER
+P:	Alan Cox
+M:	alan@the.3c501.cabal.tm
+L:	linux-net@vger.kernel.org
+S:	Maintained for 2.2 only
+
+3C505 NETWORK DRIVER
+P:	Philip Blundell
+M:	Philip.Blundell@pobox.com
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+53C700 AND 53C700-66 SCSI DRIVER
+P:	James E.J. Bottomley
+M:	James.Bottomley@HansenPartnership.com
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+6PACK NETWORK DRIVER FOR AX.25
+P:	Andreas Koensgen
+M:	ajk@iehk.rwth-aachen.de
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+8139CP 10/100 FAST ETHERNET DRIVER
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+S:	Maintained
+
+8139TOO 10/100 FAST ETHERNET DRIVER
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+W:	http://sourceforge.net/projects/gkernel/
+S:	Maintained
+
+8250/16?50 (AND CLONE UARTS) SERIAL DRIVER
+P:	Theodore Ts'o
+M:	tytso@mit.edu
+L:	linux-serial@vger.kernel.org
+W:	http://serial.sourceforge.net
+S:	Maintained
+
+8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
+P:	Paul Gortmaker
+M:	p_gortmaker@yahoo.com
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+A2232 SERIAL BOARD DRIVER
+P:	Enver Haase
+M:	ehaase@inf.fu-berlin.de
+M:	A2232@gmx.net
+L:	linux-m68k@lists.linux-m68k.org
+S:	Maintained
+
+AIO
+P:	Benjamin LaHaise
+M:	bcrl@redhat.com
+L:	linux-aio@kvack.org
+S:	Supported
+
+ACENIC DRIVER
+P:	Jes Sorensen
+M:	jes@trained-monkey.org
+L:	linux-acenic@sunsite.dk
+S:	Maintained
+
+ACI MIXER DRIVER
+P:	Robert Siemer
+M:	Robert.Siemer@gmx.de
+L:	linux-sound@vger.kernel.org
+W:	http://www.uni-karlsruhe.de/~Robert.Siemer/Private/
+S:	Maintained
+
+ACP/MWAVE MODEM
+P:	Paul B Schroeder
+M:	paulsch@us.ibm.com
+P:	Mike Sullivan
+M:	sullivam@us.ibm.com
+W:	http://www.ibm.com/linux/ltc/
+S:	Supported
+
+AACRAID SCSI RAID DRIVER
+P:	Adaptec OEM Raid Solutions
+M:	linux-aacraid-devel@dell.com
+L:	linux-aacraid-devel@dell.com
+L:	linux-aacraid-announce@dell.com
+W:	http://domsch.com/linux
+S:	Supported
+
+ACPI
+P:	Andy Grover
+M:	andrew.grover@intel.com
+L:	acpi-devel@lists.sourceforge.net
+W:	http://sf.net/projects/acpi/
+S:	Maintained
+
+AD1816 SOUND DRIVER
+P:	Thorsten Knabe
+W:	http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html
+W:	http://www.tu-darmstadt.de/~tek01/projects/linux.html
+S:	Maintained
+
+ADVANSYS SCSI DRIVER
+P:	Bob Frey
+M:	linux@advansys.com
+W:	http://www.advansys.com/linux.html
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+AEDSP16 DRIVER
+P:	Riccardo Facchetti
+M:	fizban@tin.it
+S:	Maintained
+
+AFFS FILE SYSTEM
+P:	Roman Zippel
+M:	zippel@linux-m68k.org
+S:	Maintained
+
+AHA152X SCSI DRIVER
+P:	Juergen E. Fischer
+M:	Juergen Fischer <fischer@norbit.de>
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+APM DRIVER
+P:	Stephen Rothwell
+M:	sfr@canb.auug.org.au
+L:	linux-laptop@vger.kernel.org
+W:	http://www.canb.auug.org.au/~sfr/
+S:	Supported
+
+APPLETALK NETWORK LAYER
+P:	Jay Schulist
+M:	jschlst@samba.org
+L:	linux-atalk@lists.netspace.org
+S:	Maintained
+
+ARM MFM AND FLOPPY DRIVERS
+P:	Dave Gilbert
+M:	linux@treblig.org
+S:	Maintained
+
+ARM/SHARK MACHINE SUPPORT
+P:	Alexander Schulz
+M:	alex@shark-linux.de
+W:	http://www.shark-linux.de/shark.html
+S:	Maintained
+
+ARM/STRONGARM110 PORT
+P:	Russell King
+M:	rmk@arm.linux.org.uk
+L:	linux-arm-kernel@lists.arm.linux.org.uk
+W:	http://www.arm.linux.org.uk/
+S:	Maintained
+
+ARPD SUPPORT
+P:	Jonathan Layes
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+AX.25 NETWORK LAYER
+P:	Matthias Welwarsky
+M:	dg2fef@afthd.tu-darmstadt.de
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+BAYCOM/HDLCDRV/SOUNDMODEM DRIVERS FOR AX.25
+P:	Thomas Sailer
+M:	sailer@ife.ee.ethz.ch
+L:	linux-hams@vger.kernel.org
+W:	http://www.ife.ee.ethz.ch/~sailer/ham/ham.html
+S:	Maintained
+
+BERKSHIRE PRODUCTS PC WATCHDOG DRIVER
+P:	Kenji Hollis
+W:	http://ftp.bitgate.com/pcwd/
+S:	Maintained
+
+BFS FILE SYSTEM
+P:	Tigran A. Aivazian
+M:	tigran@veritas.com
+L:	linux-kernel@vger.kernel.org
+W:	http://www.ocston.org/~tigran/patches/bfs
+S:	Maintained
+
+BLOCK LAYER
+P:	Jens Axboe
+M:	axboe@suse.de
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+BLUETOOTH SUBSYSTEM (BlueZ)
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
+W:	http://bluez.sf.net
+S:	Maintained
+
+BLUETOOTH SUBSYSTEM (PC Card Drivers)
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+W:	http://www.holtmann.org/linux/bluetooth/
+S:	Maintained
+
+BTTV VIDEO4LINUX DRIVER
+P:	Gerd Knorr
+M:	kraxel@bytesex.org
+L:	video4linux-list@redhat.com
+W:	http://bytesex.org/bttv/
+S:	Maintained
+
+BUSLOGIC SCSI DRIVER
+P:	Leonard N. Zubkoff
+M:	Leonard N. Zubkoff <lnz@dandelion.com>
+L:	linux-scsi@vger.kernel.org
+W:	http://www.dandelion.com/Linux/
+S:	Maintained
+
+CIRRUS LOGIC GENERIC FBDEV DRIVER
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+L:	linux-fbdev-devel@lists.sourceforge.net
+S:	Odd Fixes
+
+CIRRUS LOGIC CS4280/CS461x SOUNDDRIVER
+P:	Cirrus Logic Corporation (kernel 2.2 driver)
+M:	Cirrus Logic Corporation, Thomas Woller <twoller@crystal.cirrus.com>
+P:	Nils Faerber (port to kernel 2.4)
+M:	Nils Faerber <nils@kernelconcepts.de>
+S:	Maintained
+
+CODA FILE SYSTEM
+P:	Jan Harkes
+M:	jaharkes@cs.cmu.edu
+M:	coda@cs.cmu.edu
+L:	codalist@coda.cs.cmu.edu
+W:	http://www.coda.cs.cmu.edu/
+S:	Maintained
+
+COMPAQ FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA
+P:	Amy Vanzant-Hodge 
+M:	Amy Vanzant-Hodge (fibrechannel@compaq.com)
+L:	compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux
+S:	Supported
+
+COMPAQ SMART2 RAID DRIVER
+P:	Charles White	
+M:	Charles White <arrays@compaq.com>
+L:	compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux
+S:	Supported	
+
+COMPAQ SMART CISS RAID DRIVER 
+P:	Charles White
+M:	Charles White <arrays@compaq.com>
+L:	compaqandlinux@cpqlin.van-dijk.net
+W:	ftp.compaq.com/pub/products/drivers/linux	
+S:	Supported 
+
+COMPUTONE INTELLIPORT MULTIPORT CARD
+P:	Michael H. Warfield
+M:	Michael H. Warfield <mhw@wittsend.com>
+W:	http://www.computone.com/
+W:	http://www.wittsend.com/computone.html
+L:	linux-computone@lazuli.wittsend.com
+S:	Supported
+
+COMX/MULTIGATE SYNC SERIAL DRIVERS
+P:	Gergely Madarasz
+M:	Gergely Madarasz <gorgo@itc.hu>
+S:	Supported
+
+CONFIGURE, MENUCONFIG, XCONFIG
+P:	Michael Elizabeth Chastain
+M:	mec@shout.net
+L:	kbuild-devel@lists.sourceforge.net
+W:	http://kbuild.sourceforge.net
+S:	Maintained
+
+COSA/SRP SYNC SERIAL DRIVER
+P:	Jan "Yenya" Kasprzak
+M:	kas@fi.muni.cz
+W:	http://www.fi.muni.cz/~kas/cosa/
+S:	Maintained
+
+CPUID/MSR DRIVER
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+S:	Maintained
+
+CRAMFS FILESYSTEM
+P:	Daniel Quinlan
+M:	quinlan@transmeta.com
+W:	http://sourceforge.net/projects/cramfs/
+S:	Maintained
+
+CREDITS FILE
+P:	John A. Martin
+M:	jam@acm.org
+S:	Maintained
+
+CRIS PORT
+P:	Bjorn Wesen
+M:	bjornw@axis.com
+L:	dev-etrax@axis.com
+W:	http://developer.axis.com
+S:	Maintained
+
+CYBERPRO FB DRIVER
+P:	Russell King
+M:	rmk@arm.linux.org.uk
+W:	http://www.arm.linux.org.uk/
+S:	Maintained
+
+CYCLADES 2X SYNC CARD DRIVER
+P:	Arnaldo Carvalho de Melo
+M:	acme@conectiva.com.br
+W:	http://www.conectiva.com.br/~acme
+L:	cycsyn-devel@bazar.conectiva.com.br
+S:	Maintained
+
+CYCLADES ASYNC MUX DRIVER
+P:	Henrique Gobbi
+M:	henrique@cyclades.com
+W:	http://www.cyclades.com/
+S:	Supported
+
+CYCLADES PC300 DRIVER
+P:	Henrique Gobbi
+M:	henrique@cyclades.com
+W:	http://www.cyclades.com/
+S:	Supported
+
+DAMA SLAVE for AX.25
+P:	Joerg Reuter
+M:	jreuter@yaina.de
+W:	http://yaina.de/jreuter/
+W:	http://www.qsl.net/dl1bke/
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+DC390/AM53C974 SCSI driver
+P:	Kurt Garloff
+M:	garloff@suse.de
+W:	http://www.garloff.de/kurt/linux/dc390/
+S:	Maintained
+
+DECnet NETWORK LAYER
+P:	Steven Whitehouse
+M:	SteveW@ACM.org
+W:	http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html
+L:	linux-decnet-user@lists.sourceforge.net
+S:	Maintained
+
+DELL LAPTOP SMM DRIVER
+P:	Massimo Dal Zotto
+M:	dz@debian.org
+W:	http://www.debian.org/~dz/i8k/
+S:	Maintained
+
+DEVICE NUMBER REGISTRY
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+DEVICE FILESYSTEM
+P:	Richard Gooch
+M:	rgooch@atnf.csiro.au
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+DIGI INTL. EPCA DRIVER
+P:	Chad Schwartz
+M:	support@dgii.com
+L:	digilnux@dgii.com
+S:	Maintained
+
+DIGI RIGHTSWITCH NETWORK DRIVER
+P:	Rick Richardson
+L:	linux-net@vger.kernel.org
+W:	http://www.dgii.com/linux/
+S:	Maintained
+
+DIGIBOARD PC/XE AND PC/XI DRIVER
+P:	Christoph Lameter
+M:	christoph@lameter.com
+W:	http://www.dgii.com/linux,http://lameter.com/digi
+L:	digilnux@dgii.com
+S:	Maintained
+
+DIRECTORY NOTIFICATION
+P:	Stephen Rothwell
+M:	sfr@canb.auug.org.au
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+
+DISK GEOMETRY AND PARTITION HANDLING
+P:	Andries Brouwer
+M:	aeb@cwi.nl
+W:	http://www.win.tue.nl/~aeb/linux/Large-Disk.html
+W:	http://www.win.tue.nl/~aeb/linux/zip/zip-1.html
+W:	http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+S:	Maintained
+
+DISKQUOTA:
+P:	Marco van Wieringen
+M:	mvw@planets.elm.net
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
+P:	Tobias Ringstrom
+M:	tori@unhappy.mine.nu
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+DOUBLETALK DRIVER
+P:	James R. Van Zandt
+M:	jrv@vanzandt.mv.com
+L:	blinux-list@redhat.com
+S:	Maintained
+
+DRM DRIVERS
+P:	Rik Faith
+M:	faith@redhat.com
+L:	dri-devel@lists.sourceforge.net
+S:	Supported
+
+DSCC4 DRIVER
+P:	François Romieu
+M:	romieu@cogenit.fr
+M:	romieu@ensta.fr
+S:	Maintained
+
+EATA-DMA SCSI DRIVER
+P:	Michael Neuffer
+L:	linux-eata@i-connect.net, linux-scsi@vger.kernel.org
+S:	Maintained
+
+EATA ISA/EISA/PCI SCSI DRIVER
+P:	Dario Ballabio
+M:	ballabio_dario@emc.com
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+EATA-PIO SCSI DRIVER
+P:	Michael Neuffer
+M:	mike@i-Connect.Net
+L:	linux-eata@i-connect.net, linux-scsi@vger.kernel.org
+S:	Maintained
+
+EBTABLES
+P:	Bart De Schuymer
+M:	bart.de.schuymer@pandora.be
+L:	ebtables-user@lists.sourceforge.net
+L:	ebtables-devel@lists.sourceforge.net
+W:	http://ebtables.sourceforge.net/
+S:	Maintained
+
+EEPRO100 NETWORK DRIVER
+P:	Andrey V. Savochkin
+M:	saw@saw.sw.com.sg
+S:	Maintained
+
+EMU10K1 SOUND DRIVER
+P:	Rui Sousa	
+M:	rui.p.m.sousa@clix.pt	
+L:	emu10k1-devel@opensource.creative.com
+W:	http://opensource.creative.com/
+S:	Maintained
+
+ETHEREXPRESS-16 NETWORK DRIVER
+P:	Philip Blundell
+M:	Philip.Blundell@pobox.com
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+ETHERNET BRIDGE
+P:	Lennert Buytenhek
+M:	buytenh@gnu.org
+L:	bridge@math.leidenuniv.nl
+W:	http://bridge.sourceforge.net/
+S:	Maintained
+
+ETHERTEAM 16I DRIVER
+P:	Mika Kuoppala
+M:	miku@iki.fi
+S:	Maintained
+
+EXT2 FILE SYSTEM
+P:	Remy Card
+M:	Remy.Card@linux.org
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+EXT3 FILE SYSTEM
+P:	Remy Card, Stephen Tweedie
+M:	sct@redhat.com, akpm@zip.com.au, adilger@turbolinux.com
+L:	ext3-users@redhat.com
+S:	Maintained
+
+FARSYNC SYNCHRONOUS DRIVER
+P:	Kevin Curtis
+M:	kevin.curtis@farsite.co.uk
+M:	kevin.curtis@farsite.co.uk
+W:	http://www.farsite.co.uk/
+S:	Supported
+
+FILE LOCKING (flock() and fcntl()/lockf())
+P:	Matthew Wilcox
+M:	matthew@wil.cx
+L:	linux-fsdevel@vger.kernel.org
+S:	Maintained
+
+FILESYSTEMS (VFS and infrastructure)
+P:	Alexander Viro
+M:	viro@math.psu.edu
+S:	Maintained
+
+FPU EMULATOR
+P:	Bill Metzenthen
+M:	billm@suburbia.net
+W:	http://suburbia.net/~billm/floating-point/emulator/
+S:	Maintained
+
+FRAME RELAY DLCI/FRAD (Sangoma drivers too)
+P:	Mike McLagan
+M:	mike.mclagan@linux.org
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+FREEVXFS FILESYSTEM
+P:	Christoph Hellwig
+M:	hch@infradead.org
+W:	ftp://ftp.openlinux.org/pub/people/hch/vxfs
+S:	Maintained
+
+FTAPE/QIC-117
+P:	Claus-Justus Heine
+M:	claus@momo.math.rwth-aachen.de
+L:	linux-tape@vger.kernel.org
+W:	http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/
+S:	Maintained
+
+FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit)
+P:	Rik Faith
+M:	faith@cs.unc.edu
+L:	linux-scsi@vger.kernel.org
+S:	Odd fixes (e.g., new signatures)
+
+GDT SCSI DISK ARRAY CONTROLLER DRIVER
+P:	Achim Leubner
+M:	achim@vortex.de
+L:	linux-scsi@vger.kernel.org
+W:	http://www.icp-vortex.com/
+S:	Supported
+
+GENERIC HDLC DRIVER, N2 AND C101 DRIVERS
+P:	Krzysztof Halasa
+M:	khc@pm.waw.pl
+W:	http://hq.pm.waw.pl/hdlc/
+S:	Maintained
+
+HAYES ESP SERIAL DRIVER
+P:	Andrew J. Robinson
+M:	arobinso@nyx.net
+L:	linux-kernel@vger.kernel.org
+W:	http://www.nyx.net/~arobinso
+S:	Maintained
+
+HFS FILESYSTEM
+P:	Adrian Sun
+M:	asun@cobaltnet.com
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+HGA FRAMEBUFFER DRIVER
+P:	Ferenc Bakonyi
+M:	fero@drama.obuda.kando.hu
+L:	linux-nvidia@lists.surfsouth.com
+W:	http://drama.obuda.kando.hu/~fero/cgi-bin/hgafb.shtml
+S:	Maintained
+
+HIGH-SPEED SCC DRIVER FOR AX.25
+P:	Klaus Kudielka
+M:	klaus.kudielka@ieee.org
+L:	linux-hams@vger.kernel.org
+W:	http://www.nt.tuwien.ac.at/~kkudielk/Linux/
+S:	Maintained
+
+HIPPI
+P:	Jes Sorensen
+M:	jes@trained-monkey.org
+L:	linux-hippi@sunsite.dk
+S:	Maintained
+
+HP100:	Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
+P:	Jaroslav Kysela
+M:	perex@suse.cz
+S:	Maintained
+
+HPFS FILESYSTEM
+P:	Mikulas Patocka
+M:	mikulas@artax.karlin.mff.cuni.cz
+W:	http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
+S:	Maintained
+
+HPUSBSCSI
+P:	Oliver Neukum
+M:	drivers@neukum.org
+S:	Maintained
+
+I2C DRIVERS
+P:	Simon Vogl
+M:	simon@tk.uni-linz.ac.at
+P:	Frodo Looijaard
+M:	frodol@dds.nl
+L:	linux-i2c@pelican.tk.uni-linz.ac.at
+W:	http://www.tk.uni-linz.ac.at/~simon/private/i2c
+S:	Maintained
+
+i386 BOOT CODE
+P:	Riley H. Williams
+M:	rhw@memalpha.cx
+L:	Linux-Kernel@vger.kernel.org
+S:	Maintained
+
+i386 SETUP CODE / CPU ERRATA WORKAROUNDS
+P:	Dave Jones
+M:	davej@suse.de
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+S:	Maintained
+
+i810 TCO TIMER WATCHDOG
+P:	Nils Faerber
+M:	nils@kernelconcepts.de
+W:	http://www.kernelconcepts.de/
+S:	Maintained
+
+IA64 (Itanium) PLATFORM
+P:	David Mosberger-Tang
+M:	davidm@hpl.hp.com
+L:	linux-ia64@linuxia64.org
+W:	http://www.linuxia64.org/
+S:	Maintained
+
+IBM MCA SCSI SUBSYSTEM DRIVER
+P:	Michael Lang
+M:	langa2@kph.uni-mainz.de
+W:	http://www.uni-mainz.de/~langm000/linux.html
+S:	Maintained
+
+IBM ServeRAID RAID DRIVER
+P: 	Jack Hammer
+P:	Dave Jeffrey	
+M:	ipslinux@us.ibm.com
+W:	http://www.developer.ibm.com/welcome/netfinity/serveraid.html
+S:	Supported 
+
+IDE DRIVER [GENERAL]
+P:	Martin Dalecki
+M:	martin@dalecki.de
+I:	pl_PL.ISO8859-2, de_DE.ISO8859-15, (en_US.ISO8859-1)
+L:	linux-kernel@vger.kernel.org
+W:	http://www.dalecki.de
+S:	Developement
+
+IDE/ATAPI CDROM DRIVER
+P:	Jens Axboe
+M:	axboe@suse.de
+L:	linux-kernel@vger.kernel.org
+W:	http://www.kernel.dk
+S:	Maintained
+
+IDE/ATAPI FLOPPY DRIVERS
+P:	Paul Bristow
+M:	Paul Bristow <paul@paulbristow.net>
+W:	http://paulbristow.net/linux/idefloppy.html
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+IDE/ATAPI TAPE DRIVERS
+P:	Gadi Oxman
+M:	Gadi Oxman <gadio@netvision.net.il>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+IEEE 1394 SUBSYSTEM
+P:	Ben Collins
+M:	bcollins@debian.org
+L:	linux1394-devel@lists.sourceforge.net
+W:	http://linux1394.sourceforge.net/
+S:	Maintained
+
+IEEE 1394 OHCI DRIVER
+P:	Ben Collins
+M:	bcollins@debian.org
+L:	linux1394-devel@lists.sourceforge.net
+S:	Maintained
+
+IEEE 1394 PCILYNX DRIVER
+P:	Andreas Bombe
+M:	andreas.bombe@munich.netsurf.de
+L:	linux1394-devel@lists.sourceforge.net
+S:	Maintained
+
+IEEE 1394 RAW I/O DRIVER
+P:	Andreas Bombe
+M:	andreas.bombe@munich.netsurf.de
+L:	linux1394-devel@lists.sourceforge.net
+S:	Maintained
+
+IMS TWINTURBO FRAMEBUFFER DRIVER
+P:	Paul Mundt
+M:	lethal@chaoticdreams.org
+L:	linux-fbdev-devel@lists.sourceforge.net
+S:	Maintained
+
+INTEL APIC/IOAPIC, LOWLEVEL X86 SMP SUPPORT
+P:	Ingo Molnar
+M:	mingo@redhat.com
+S:	Maintained
+
+INTEL I8XX RANDOM NUMBER GENERATOR SUPPORT
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+W:	http://sourceforge.net/projects/gkernel/
+S:	Maintained
+
+INTEL IA32 MICROCODE UPDATE SUPPORT
+P:	Tigran Aivazian
+M:	tigran@veritas.com
+S:	Maintained
+
+INTEL PRO/100 ETHERNET SUPPORT
+P:	Scott Feldman
+M:	scott.feldman@intel.com
+S:	Supported
+
+INTEL PRO/1000 GIGABIT ETHERNET SUPPORT
+P:	Chris Leech
+M:	christopher.leech@intel.com
+P:	Scott Feldman
+M:	scott.feldman@intel.com
+S:	Supported
+
+INTERMEZZO FILE SYSTEM
+P:	Peter J. Braam
+M:	braam@clusterfs.com
+W:	http://www.inter-mezzo.org/
+L:	intermezzo-discuss@lists.sourceforge.net
+S:	Maintained
+
+IOC3 DRIVER
+P:	Ralf Baechle
+M:	ralf@oss.sgi.com
+L:	linux-mips@oss.sgi.com
+S:	Maintained
+
+IP MASQUERADING:
+P:	Juanjo Ciarlante
+M:	jjciarla@raiz.uncu.edu.ar
+S:	Maintained
+
+IPX NETWORK LAYER
+P:	Arnaldo Carvalho de Melo
+M:	acme@conectiva.com.br
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+IRDA SUBSYSTEM
+P:	Dag Brattli
+M:	Dag Brattli <dag@brattli.net>
+L:	linux-irda@pasta.cs.uit.no
+W:	http://irda.sourceforge.net/
+S:	Maintained
+
+ISAPNP
+P:	Jaroslav Kysela
+M:	perex@suse.cz
+S:	Maintained
+
+ISDN SUBSYSTEM
+P:	Karsten Keil
+M:	kkeil@suse.de
+P:	Kai Germaschewski
+M:	kai.germaschewski@gmx.de
+L:	isdn4linux@listserv.isdn4linux.de
+W:	http://www.isdn4linux.de
+S:	Maintained
+
+ISDN SUBSYSTEM (Eicon active card driver)
+P:	Armin Schindler
+M:	mac@melware.de
+L:	isdn4linux@listserv.isdn4linux.de
+W:	http://www.melware.de
+S:	Maintained
+
+JOURNALLING FLASH FILE SYSTEM (JFFS)
+P:	Axis Communications AB
+M:	jffs-dev@axis.com
+L:	jffs-dev@axis.com
+W:	http://www.developer.axis.com/software/jffs/
+S:	Maintained
+
+JOURNALLING FLASH FILE SYSTEM V2 (JFFS2)
+P:	David Woodhouse
+M:	dwmw2@infradead.org
+L:	jffs-dev@axis.com
+W:	http://sources.redhat.com/jffs2/
+S:	Maintained
+
+JFS FILESYSTEM
+P:	Dave Kleikamp
+M:	shaggy@austin.ibm.com
+L:	jfs-discussion@oss.software.ibm.com
+W:	http://oss.software.ibm.com/jfs/
+S:	Supported
+
+JOYSTICK DRIVER
+P:	Vojtech Pavlik
+M:	vojtech@suse.cz
+L:	linux-joystick@atrey.karlin.mff.cuni.cz
+W:	http://www.suse.cz/development/joystick/
+S:	Maintained
+
+KERNEL AUTOMOUNTER (AUTOFS)
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	autofs@linux.kernel.org
+S:	Maintained
+
+KERNEL AUTOMOUNTER v4 (AUTOFS4)
+P:	Jeremy Fitzhardinge
+M:	jeremy@goop.org
+L:	autofs@linux.kernel.org
+S:	Maintained
+
+KERNEL BUILD (Makefile, Rules.make, scripts/*)
+P:	Keith Owens
+M:	kaos@ocs.com.au
+P:	Michael Elizabeth Chastain
+M:	mec@shout.net
+L:	kbuild-devel@lists.sourceforge.net
+W:	http://kbuild.sourceforge.net
+S:	Maintained 
+
+KERNEL NFSD
+P:	Neil Brown
+M:	neilb@cse.unsw.edu.au
+L:	nfs-devel@linux.kernel.org (Linux NFS)
+L:	nfs@lists.sourceforge.net
+W:	http://nfs.sourceforge.net/
+W:	http://www.cse.unsw.edu.au/~neilb/oss/knfsd/
+S:	Maintained
+
+LANMEDIA WAN CARD DRIVER
+P:	Andrew Stanley-Jones
+M:	asj@lanmedia.com
+W:	http://www.lanmedia.com/
+S:	Supported
+ 
+LAPB module
+P:	Henner Eisen
+M:	eis@baty.hanse.de
+L:	linux-x25@vger.kernel.org
+S:	Maintained
+
+LASI 53c700 driver for PARISC
+P:	James E.J. Bottomley
+M:	James.Bottomley@HansenPartnership.com
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+LINUX FOR IBM pSERIES (RS/6000)
+P:	Paul Mackerras
+M:	paulus@au.ibm.com
+W:	http://www.ibm.com/linux/ltc/projects/ppc
+S:	Supported
+
+LINUX FOR POWERPC
+P:	Paul Mackerras
+M:	paulus@samba.org
+W:	http://www.fsmlabs.com/linuxppcbk.html
+S:	Supported
+
+LINUX FOR POWER MACINTOSH
+P:	Benjamin Herrenschmidt
+M:	benh@kernel.crashing.org
+W:	http://www.linuxppc.org/
+L:	linuxppc-dev@lists.linuxppc.org
+S:	Maintained
+
+LLC (802.2)
+P:	Arnaldo Carvalho de Melo
+M:	acme@conectiva.com.br
+S:	Maintained
+
+LINUX FOR 64BIT POWERPC
+P:	David Engebretsen
+M:	engebret@us.ibm.com
+W:	http://linuxppc64.org
+L:	linuxppc64-dev@lists.linuxppc.org
+S:	Supported
+
+LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks)
+P:	Richard Russon (FlatCap)
+M:	ldm@flatcap.org
+L:	ldm-devel@lists.sourceforge.net	
+W:	http://ldm.sourceforge.net
+S:	Maintained
+
+LOGICAL VOLUME MANAGER
+P:	Heinz Mauelshagen
+L:	linux-LVM@sistina.com
+W:	http://www.sistina.com/lvm
+S:	Maintained 
+
+LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers
+P:	Gerard Roudier
+M:	groudier@free.fr
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+M68K
+P:	Jes Sorensen
+M:	jes@trained-monkey.org
+W:	http://www.clark.net/pub/lawrencc/linux/index.html
+L:	linux-m68k@lists.linux-m68k.org
+S:	Maintained
+
+M68K ON APPLE MACINTOSH
+P:	Joshua Thompson
+M:	funaho@jurai.org
+W:	http://www.mac.linux-m68k.org/
+L:	linux-mac68k@mac.linux-m68k.org
+S:	Maintained
+
+M68K ON HP9000/300
+P:	Philip Blundell
+M:	philb@gnu.org
+W:	http://www.tazenda.demon.co.uk/phil/linux-hp
+S:	Maintained
+
+MAESTRO PCI SOUND DRIVERS
+P:	Zach Brown
+M:	zab@zabbo.net
+S:	Odd Fixes
+
+MATROX FRAMEBUFFER DRIVER
+P:	Petr Vandrovec
+M:	vandrove@vc.cvut.cz
+L:	linux-fbdev-devel@lists.sourceforge.net
+S:	Maintained
+
+MEMORY TECHNOLOGY DEVICES
+P:	David Woodhouse
+M:	dwmw2@redhat.com
+W:	http://www.linux-mtd.infradead.org/
+L:	mtd@infradead.org
+S:	Maintained
+
+MICROTEK X6 SCANNER
+P:	Oliver Neukum
+M:	drivers@neukum.org
+S:	Maintained
+
+MIPS
+P:	Ralf Baechle
+M:	ralf@gnu.org
+W:	http://oss.sgi.com/mips/mips-howto.html
+L:	linux-mips@oss.sgi.com
+S:	Maintained
+
+MISCELLANEOUS MCA-SUPPORT
+P:	David Weinehall
+M:	Project MCA Team <mcalinux@acc.umu.se>
+M:	David Weinehall <tao@acc.umu.se>
+W:	http://www.acc.umu.se/~tao/
+W:	http://www.acc.umu.se/~mcalinux/
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+MODULE SUPPORT [GENERAL], KMOD
+P:	Keith Owens
+M:	kaos@ocs.com.au
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+MOUSE AND MISC DEVICES [GENERAL]
+P:	Alessandro Rubini
+M:	rubini@ipvvis.unipv.it
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+MTRR AND SIMILAR SUPPORT [i386]
+P:	Richard Gooch
+M:	rgooch@atnf.csiro.au
+L:	linux-kernel@vger.kernel.org
+W:	http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
+S:	Maintained
+
+MULTISOUND SOUND DRIVER
+P:	Andrew Veliath
+M:	andrewtv@usa.net
+S:	Maintained
+
+NATSEMI ETHERNET DRIVER (DP8381x)
+P: 	Tim Hockin
+M:  thockin@hockin.org
+S:  Maintained
+
+NCP FILESYSTEM
+P:	Petr Vandrovec
+M:	vandrove@vc.cvut.cz
+P:	Volker Lendecke
+M:	vl@kki.org
+L:	linware@sh.cvut.cz
+S:	Maintained
+
+NCR DUAL 700 SCSI DRIVER (MICROCHANNEL)
+P:	James E.J. Bottomley
+M:	James.Bottomley@HansenPartnership.com
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+NETFILTER/IPTABLES
+P:	Rusty Russell
+P:	Marc Boucher
+P:	James Morris
+P:	Harald Welte
+P:	Jozsef Kadlecsik
+M:	coreteam@netfilter.org
+W:	http://www.netfilter.org/
+W:	http://www.iptables.org/
+L:	netfilter@lists.netfilter.org
+L:	netfilter-devel@lists.netfilter.org
+S:	Supported
+
+NETROM NETWORK LAYER
+P:	Tomi Manninen
+M:	Tomi.Manninen@hut.fi
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+NETWORK BLOCK DEVICE
+P:	Pavel Machek
+M:	pavel@atrey.karlin.mff.cuni.cz
+S:	Maintained
+
+NETWORK DEVICE DRIVERS
+P:	Andrew Morton
+M:	akpm@zip.com.au
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+NETWORKING [GENERAL]
+P:	Networking Team
+M:	netdev@oss.sgi.com
+L:	linux-net@vger.kernel.org
+W:	http://www.uk.linux.org/NetNews.html (2.0 only)
+S:	Maintained
+
+NETWORKING [IPv4/IPv6]
+P:	David S. Miller
+M:	davem@redhat.com
+P:	Alexey Kuznetsov
+M:	kuznet@ms2.inr.ac.ru
+P:	Pekka Savola (ipv6)
+M:	pekkas@netcore.fi
+L:	netdev@oss.sgi.com
+S:	Maintained
+
+NFS CLIENT
+P:	Trond Myklebust
+M:	trond.myklebust@fys.uio.no
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+NI5010 NETWORK DRIVER
+P:	Jan-Pascal van Best and Andreas Mohr
+M:	Jan-Pascal van Best <jvbest@qv3pluto.leidenuniv.nl>
+M:	Andreas Mohr <100.30936@germany.net>
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+NINJA SCSI-3 / NINJA SCSI-32Bi PCMCIA SCSI HOST ADAPTER DRIVER
+P:	YOKOTA Hiroshi
+M:	yokota@netlab.is.tsukuba.ac.jp
+W:	http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
+S:	Maintained
+
+NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
+P:	Eberhard Moenkeberg
+M:	emoenke@gwdg.de
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+NTFS FILESYSTEM
+P:	Anton Altaparmakov
+M:	aia21@cantab.net
+L:	linux-ntfs-dev@lists.sourceforge.net
+L:	linux-kernel@vger.kernel.org
+W:	http://linux-ntfs.sf.net/
+S:	Maintained
+
+NVIDIA (RIVA) FRAMEBUFFER DRIVER
+P:	Ani Joshi
+M:	ajoshi@shell.unixbox.com
+L:	linux-nvidia@lists.surfsouth.com
+S:	Maintained
+
+OLYMPIC NETWORK DRIVER
+P:	Peter De Shrijver
+M:	p2@ace.ulyssis.student.kuleuven.ac.be
+P:	Mike Phillips
+M:	mikep@linuxtr.net 
+L:	linux-net@vger.kernel.org
+L:	linux-tr@linuxtr.net
+W:	http://www.linuxtr.net
+S:	Maintained
+
+ONSTREAM SCSI TAPE DRIVER
+P:	Willem Riede
+M:	osst@riede.org
+L:	osst@linux1.onstream.nl
+L:	linux-scsi@vger.rutgers.edu
+S:	Maintained
+
+OPL3-SA2, SA3, and SAx DRIVER
+P:	Zwane Mwaikambo
+M:	zwane@commfireservices.com
+L:	linux-sound@vger.kernel.org
+S:	Maintained
+
+ORINOCO DRIVER
+P:	David Gibson
+M:	hermes@gibson.dropbear.id.au
+W:	http://www.ozlabs.org/people/dgibson/dldwd
+S:	Maintained
+
+PARALLEL PORT SUPPORT
+P:	Phil Blundell
+M:	Philip.Blundell@pobox.com
+P:	Tim Waugh
+M:	tim@cyberelk.net
+P:	David Campbell
+M:	campbell@torque.net
+P:	Andrea Arcangeli
+M:	andrea@e-mind.com
+L:	linux-parport@torque.net
+W:	http://people.redhat.com/twaugh/parport/
+S:	Maintained
+
+PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
+P:	Tim Waugh
+M:	tim@cyberelk.net
+L:	linux-parport@torque.net
+W:	http://www.torque.net/linux-pp.html
+S:	Maintained
+
+PERSONALITY HANDLING
+P:	Christoph Hellwig
+M:	hch@infradead.org
+L:	linux-abi-devel@lists.sourceforge.net
+S:	Maintained
+
+PCI ID DATABASE
+P:	Martin Mares
+M:	mj@ucw.cz
+L:	pciids-devel@lists.sourceforge.net
+W:	http://pciids.sourceforge.net/
+S:	Maintained
+
+PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
+P:	Thomas Sailer
+M:	sailer@ife.ee.ethz.ch
+L:	linux-sound@vger.kernel.org
+W:	http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html
+S:	Maintained
+
+PCI SUBSYSTEM
+P:	Martin Mares
+M:	mj@ucw.cz
+L:	linux-kernel@vger.kernel.org
+S:	Odd Fixes
+
+PCI HOTPLUG CORE
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+M:	gregkh@us.ibm.com
+S:	Supported
+
+PCI HOTPLUG COMPAQ DRIVER
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+S:	Maintained
+
+PCMCIA SUBSYSTEM
+P:	David Hinds
+M:	dhinds@zen.stanford.edu
+L:	linux-kernel@vger.kernel.org
+W:	http://pcmcia-cs.sourceforge.net
+S:	Maintained
+
+PCNET32 NETWORK DRIVER
+P:	Thomas Bogendörfer
+M:	tsbogend@alpha.franken.de
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+PNP SUPPORT
+P:	Tom Lees
+M:	tom@lpsg.demon.co.uk
+L:	pnp-users@ferret.lmh.ox.ac.uk
+L:	pnp-devel@ferret.lmh.ox.ac.uk
+W:	http://www-jcr.lmh.ox.ac.uk/~pnp/
+S:	Maintained
+
+PPP PROTOCOL DRIVERS AND COMPRESSORS
+P:	Paul Mackerras
+M:	paulus@samba.org
+L:	linux-ppp@vger.kernel.org
+S:	Maintained
+
+PPP OVER ATM (RFC 2364)
+P:	Mitchell Blank Jr
+M:	mitch@sfgoth.com
+S:	Maintained
+
+PPP OVER ETHERNET
+P:	Michal Ostrowski
+M:	mostrows@styx.uwaterloo.ca
+S:	Maintained
+
+PREEMPTIBLE KERNEL
+P:	Robert Love
+M:	rml@tech9.net
+L:	linux-kernel@vger.kernel.org
+L:	kpreempt-tech@lists.sourceforge.net
+W:	ftp://ftp.kernel.org/pub/linux/kernel/people/rml/preempt-kernel
+S:	Supported
+
+PROMISE DC4030 CACHING DISK CONTROLLER DRIVER
+P:	Peter Denison
+M:	promise@pnd-pc.demon.co.uk
+W:	http://www.pnd-pc.demon.co.uk/promise/
+S:	Maintained
+
+QNX4 FILESYSTEM
+P:	Anders Larsen
+M:	al@alarsen.net
+L:	linux-kernel@vger.kernel.org
+W:	http://www.alarsen.net/linux/qnx4fs/
+S:	Maintained
+
+RADEON FRAMEBUFFER DISPLAY DRIVER
+P:  Ani Joshi
+M:  ajoshi@shell.unixbox.com
+L:  linux-fbdev-devel@lists.sourceforge.net
+S:  Maintained
+
+RAGE128 FRAMEBUFFER DISPLAY DRIVER
+P:	Ani Joshi
+M:	ajoshi@shell.unixbox.com
+L:	linux-fbdev-devel@lists.sourceforge.net
+S:	Maintained
+
+RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
+P:	Corey Thomas
+M:	corey@world.std.com
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+REAL TIME CLOCK DRIVER
+P:	Paul Gortmaker
+M:	p_gortmaker@yahoo.com
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+REISERFS FILE SYSTEM
+P:	Hans Reiser
+M:	reiserfs-dev@namesys.com
+L:	reiserfs-list@namesys.com
+W:	http://www.namesys.com
+S:	Supported
+
+ROSE NETWORK LAYER
+P:	Jean-Paul Roubelat
+M:	jpr@f6fbb.org
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+RISCOM8 DRIVER
+P:	Dmitry Gorodchanin
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+RTLINUX  REALTIME  LINUX
+P:	Victor Yodaiken
+M:	yodaiken@fsmlabs.com
+L:	rtl@rtlinux.org
+W:	www.rtlinux.org
+S:	Maintained
+
+S390
+P:	Martin Schwidefsky
+M:	schwidefsky@de.ibm.com
+M:	linux390@de.ibm.com
+L:	linux-390@vm.marist.edu
+W:	http://oss.software.ibm.com/developerworks/opensource/linux390
+S:	Supported
+
+SA1100 SUPPORT
+P:	Nicolas Pitre
+M:	nico@cam.org
+L:	sa1100-linux@pa.dec.com
+S:	Maintained
+
+SBPCD CDROM DRIVER
+P:	Eberhard Moenkeberg
+M:	emoenke@gwdg.de
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+SCHEDULER
+P:	Ingo Molnar
+M:	mingo@elte.hu
+P:	Robert Love    [the preemptible kernel bits]
+M:	rml@tech9.net
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+SCSI CDROM DRIVER
+P:	Jens Axboe
+M:	axboe@suse.de
+L:	linux-scsi@vger.kernel.org
+W:	http://www.kernel.dk
+S:	Maintained
+
+SCSI SG DRIVER
+P:	Doug Gilbert
+M:	dgilbert@interlog.com
+L:	linux-scsi@vger.kernel.org
+W:	http://www.torque.net/sg
+S:	Maintained
+
+SCSI SUBSYSTEM
+P:	Jens Axboe
+M:	axboe@suse.de
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+SCSI TAPE DRIVER
+P:	Kai Mäkisara
+M:	Kai.Makisara@metla.fi
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+SGI VISUAL WORKSTATION 320 AND 540
+P:	Bent Hagemark
+M:	bh@sgi.com
+P:	Ingo Molnar
+M:	mingo@redhat.com
+S:	Maintained
+
+SIS 5513 IDE CONTROLLER DRIVER
+P:	Lionel Bouton
+M:	Lionel.Bouton@inet6.fr
+W:	http://inet6.dyn.dhs.org/sponsoring/sis5513/index.html
+W:	http://gyver.homeip.net/sis5513/index.html
+S:	Maintained
+
+SIS 900/7016 FAST ETHERNET DRIVER
+P:	Ollie Lho
+M:	ollie@sis.com.tw
+L:	linux-net@vger.kernel.org
+S:	Supported
+
+SMB FILESYSTEM
+P:	Urban Widmark
+M:	urban@teststation.com
+W:	http://samba.org/
+L:	samba@samba.org
+S:	Maintained
+
+SNA NETWORK LAYER
+P:	Jay Schulist
+M:	jschlst@samba.org
+L:	linux-sna@turbolinux.com
+W:	http://www.linux-sna.org
+S:	Supported
+
+SOFTWARE RAID (Multiple Disks) SUPPORT
+P:	Ingo Molnar
+M:	mingo@redhat.com
+P:	Neil Brown
+M:	neilb@cse.unsw.edu.au
+L:	linux-raid@vger.kernel.org
+S:	Maintained
+
+SOFTWARE SUSPEND:
+P:	Pavel Machek
+M:	pavel@suse.cz
+M:	pavel@ucw.cz
+L:	http://lister.fornax.hu/mailman/listinfo/swsusp
+W:	http://swsusp.sf.net/
+S:	Maintained
+
+SONIC NETWORK DRIVER
+P:	Thomas Bogendoerfer
+M:	tsbogend@alpha.franken.de
+L:	linux-net@vger.kernel.org
+S:	Maintained
+
+SOUND
+P:	Jaroslav Kysela
+M:	perex@suse.cz
+L:	alsa-devel@alsa-project.org
+S:	Maintained
+
+UltraSPARC (sparc64):
+P:	David S. Miller
+M:	davem@redhat.com
+P:	Eddie C. Dost
+M:	ecd@skynet.be
+P:	Jakub Jelinek
+M:	jj@sunsite.ms.mff.cuni.cz
+P:	Anton Blanchard
+M:	anton@samba.org
+L:	sparclinux@vger.kernel.org
+L:	ultralinux@vger.kernel.org
+S:	Maintained
+
+SPARC (sparc32):
+S:	Unmaintained
+
+SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
+P:	Roger Wolff
+M:	R.E.Wolff@BitWizard.nl
+M:	io8-linux@specialix.co.uk
+L:	linux-kernel@vger.kernel.org ?
+S:	Supported
+
+SPX NETWORK LAYER
+P:	Jay Schulist
+M:	jschlst@samba.org
+L:	linux-net@vger.kernel.org
+S:	Supported
+
+SRM (Alpha) environment access
+P:	Jan-Benedict Glaw
+M:	jbglaw@lug-owl.de
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS
+M:	support@stallion.oz.au
+W:	http://www.stallion.com
+S:	Supported
+
+STARFIRE/DURALAN NETWORK DRIVER
+P:	Ion Badulescu
+M:	ionut@cs.columbia.edu
+S:	Maintained
+
+STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
+W:	http://mosquitonet.Stanford.EDU/strip.html
+S:	Unsupported ?
+
+STRADIS MPEG-2 DECODER DRIVER
+P:	Nathan Laredo
+M:	laredo@gnu.org
+W:	http://mpeg.openprojects.net/
+W:	http://www.stradis.com/
+S:	Maintained
+
+SUPERH
+P:	Niibe Yutaka
+M:	gniibe@m17n.org
+P:	Kazumoto Kojima
+M:	kkojima@rr.iij4u.or.jp
+L:	linux-sh@m17n.org
+W:	http://www.m17n.org/linux-sh/
+W:	http://www.rr.iij4u.or.jp/~kkojima/linux-sh4.html
+S:	Maintained
+
+SVGA HANDLING
+P:	Martin Mares
+M:	mj@ucw.cz
+L:	linux-video@atrey.karlin.mff.cuni.cz
+S:	Maintained
+
+SYSV FILESYSTEM
+P:	Christoph Hellwig
+M:	hch@infradead.org
+S:	Maintained
+
+TI GRAPH LINK USB (SilverLink) CABLE DRIVER
+P:	Romain Lievin
+M:	roms@lpg.ticalc.org
+P:	Julien Blache
+M:	jb@technologeek.org
+S:	Maintained
+
+TIEMAN VOYAGER USB BRAILLE DISPLAY DRIVER
+P:	Stephane Dalton
+M:	sdalton@videotron.ca
+P:	Stéphane Doyon
+M:	s.doyon@videotron.ca
+S:	Maintained
+
+TLAN NETWORK DRIVER
+P:	Samuel Chessman
+M:	chessman@tux.org
+L:	tlan-devel@lists.sourceforge.net
+W:	http://sourceforge.net/projects/tlan/
+S:	Maintained
+
+TOKEN-RING NETWORK DRIVER
+P:	Mike Phillips
+M:	mikep@linuxtr.net
+L:	linux-net@vger.kernel.org
+L:	linux-tr@linuxtr.net
+W:	http://www.linuxtr.net
+S:	Maintained
+
+TOSHIBA ACPI EXTRAS DRIVER
+P:	John Belmonte
+M:	toshiba_acpi@memebeam.org
+W:	http://memebeam.org/toys/ToshibaAcpiDriver
+S:	Maintained
+
+TOSHIBA SMM DRIVER
+P:	Jonathan Buzzard
+M:	jonathan@buzzard.org.uk
+L:	tlinux-users@tce.toshiba-dme.co.jp
+W:	http://www.buzzard.org.uk/toshiba/
+S:	Maintained
+
+TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE
+P:	Ollie Lho  
+M:	ollie@sis.com.tw
+L:	linux-kernel@vger.kernel.org
+S:	Supported
+
+TMS380 TOKEN-RING NETWORK DRIVER
+P:	Adam Fritzler
+M:	mid@auk.cx
+L:	linux-tr@linuxtr.net
+W:	http://www.auk.cx/tms380tr/
+S:	Maintained
+
+TULIP NETWORK DRIVER
+P:	Jeff Garzik
+M:	jgarzik@mandrakesoft.com
+L:	tulip-users@lists.sourceforge.net
+W:	http://sourceforge.net/projects/tulip/
+S:	Maintained
+
+TUN/TAP driver
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com, max_mk@yahoo.com
+L:	vtun@office.satix.net
+W:	http://vtun.sourceforge.net/tun
+S:	Maintained
+
+U14-34F SCSI DRIVER
+P:	Dario Ballabio
+M:	ballabio_dario@emc.com
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+UDF FILESYSTEM
+P:	Ben Fennema
+M:	bfennema@falcon.csc.calpoly.edu
+P:	Dave Boynton
+M:	dave@trylinux.com
+L:	linux_udf@hpesjro.fc.hp.com
+W:	http://linux-udf.sourceforge.net
+S:	Maintained
+
+UMSDOS FILESYSTEM
+P:	Matija Nalis
+M:	Matija Nalis <mnalis-umsdos@voyager.hr>
+L:	linux-kernel@vger.kernel.org
+W:	http://linux.voyager.hr/umsdos/
+S:	Maintained
+
+UNIFORM CDROM DRIVER
+P:	Jens Axboe
+M:	axboe@suse.de
+L:	linux-kernel@vger.kernel.org
+W:	http://www.kernel.dk
+S:	Maintained
+
+USB ACM DRIVER
+P:	Vojtech Pavlik
+M:	vojtech@suse.cz
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB BLUETOOTH DRIVER
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+W:	http://www.kroah.com/linux-usb/
+
+USB CDC ETHERNET DRIVER
+P:	Brad Hards
+M:	bradh@frogmouth.net
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB EHCI DRIVER
+P:	David Brownell
+M:	dbrownell@users.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB HID/HIDBP/INPUT DRIVERS
+P:	Vojtech Pavlik
+M:	vojtech@suse.cz
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://www.suse.cz/development/input/
+S:	Maintained
+
+USB HUB
+P:	Johannes Erdfelt
+M:	johannes@erdfelt.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB KAWASAKI LSI DRIVER
+P:	Oliver Neukum
+M:	drivers@neukum.org
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB MASS STORAGE DRIVER
+P:	Matthew Dharm
+M:	mdharm-usb@one-eyed-alien.net
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+W:	http://www.one-eyed-alien.net/~mdharm/linux-usb/
+
+USB OHCI DRIVER
+P:	Roman Weissgaerber
+M:	weissg@vienna.at
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB OV511 DRIVER
+P:	Mark McClelland
+M:	mmcclell@bigfoot.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://alpha.dyndns.org/ov511/
+S:	Maintained
+
+USB PEGASUS DRIVER
+P:	Petko Manolov
+M:	petkan@users.sourceforge.net
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://pegasus2.sourceforge.net/
+S:	Maintained
+
+USB PRINTER DRIVER
+P:	Vojtech Pavlik
+M:	vojtech@suse.cz
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB RTL8150 DRIVER
+P:	Petko Manolov
+M:	petkan@users.sourceforge.net
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://pegasus2.sourceforge.net/
+S:	Maintained
+
+USB SCANNER DRIVER
+P:	Brian Beattie
+M:	beattie@beattie-home.net
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://www.beattie-home.net/linux
+S:	Maintained
+
+USB SE401 DRIVER
+P:	Jeroen Vreeken
+M:	pe1rxq@amsat.org
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://www.chello.nl/~j.vreeken/se401/
+S:	Maintained
+
+USB SERIAL DIGI ACCELEPORT DRIVER
+P:	Peter Berger and Al Borchers
+M:	pberger@brimson.com
+M:	alborchers@steinerpoint.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB SERIAL DRIVER
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+W:	http://www.kroah.com/linux-usb/
+
+USB SERIAL BELKIN F5U103 DRIVER
+P:	William Greathouse
+M:	wgreathouse@smva.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB SERIAL CYBERJACK PINPAD/E-COM DRIVER
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB AUERSWALD DRIVER
+P:	Wolfgang Muees
+M:	wolfgang@iksw-muees.de
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB SERIAL EMPEG EMPEG-CAR MARK I/II DRIVER
+P:	Gary Brubaker
+M:	xavyer@ix.netcom.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USB SERIAL KEYSPAN DRIVER
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://www.kroah.com/linux/
+S:	Maintained
+
+USB SUBSYSTEM
+P:	Greg Kroah-Hartman
+M:	greg@kroah.com
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://www.linux-usb.org
+S:	Supported
+
+USB UHCI DRIVER
+P:	Georg Acher
+M:	usb@in.tum.de
+L:	linux-usb-users@lists.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+W:	http://usb.in.tum.de
+S:	Maintained
+	
+USB "USBNET" DRIVER
+P:	David Brownell
+M:	dbrownell@users.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
+USER-MODE LINUX
+P:	Jeff Dike
+M:	jdike@karaya.com
+L:	user-mode-linux-devel@lists.sourceforge.net
+L:	user-mode-linux-user@lists.sourceforge.net
+W:	http://user-mode-linux.sourceforge.net
+S:	Maintained
+	
+VFAT FILESYSTEM:
+P:	Gordon Chaffee
+M:	chaffee@cs.berkeley.edu
+L:	linux-kernel@vger.kernel.org
+W:	http://bmrc.berkeley.edu/people/chaffee
+S:	Maintained
+
+VIA 82Cxxx AUDIO DRIVER
+P:	Jeff Garzik
+L:	linux-via@gtf.org
+S:	Odd fixes
+
+USB DIAMOND RIO500 DRIVER
+P:	Cesar Miquel
+M:	miquel@df.uba.ar
+L:	rio500-users@lists.sourceforge.net
+W:	http://rio500.sourceforge.net
+S:	Maintained
+
+VIDEO FOR LINUX
+P:	Gerd Knorr
+M:	kraxel@bytesex.org
+S:	Maintained
+
+WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC)
+P:	Nenad Corbic
+M:	ncorbic@sangoma.com
+M:	dm@sangoma.com
+W:	http://www.sangoma.com
+S:	Supported
+
+WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
+P:	Jean Tourrilhes
+M:	jt@hpl.hp.com
+W:	http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/
+S:	Maintained
+
+WD7000 SCSI DRIVER
+P:	Miroslav Zagorac
+M:	zaga@fly.cc.fer.hr
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+
+X.25 NETWORK LAYER
+P:	Henner Eisen
+M:	eis@baty.hanse.de
+L:	linux-x25@vger.kernel.org
+S:	Maintained
+
+X86 3-LEVEL PAGING (PAE) SUPPORT
+P:	Ingo Molnar
+M:	mingo@redhat.com
+S:	Maintained
+
+X86-64 port
+P:	Andi Kleen
+M:	ak@suse.de
+L:	discuss@x86-64.org
+W:	http://www.x86-64.org
+S:	Maintained
+
+YAM DRIVER FOR AX.25
+P:	Jean-Paul Roubelat
+M:	jpr@f6fbb.org
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+YMFPCI YAMAHA PCI SOUND
+P:	Pete Zaitcev
+M:	zaitcev@yahoo.com
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+
+Z85230 SYNCHRONOUS DRIVER
+P:	Alan Cox
+M:	alan@redhat.com
+W:	http://roadrunner.swansea.linux.org.uk/synchronous.shtml
+S:	Maintained for 2.2 only
+
+Z8530 DRIVER FOR AX.25
+P:	Joerg Reuter
+M:	jreuter@yaina.de
+W:	http://yaina.de/jreuter/
+W:	http://www.qsl.net/dl1bke/
+L:	linux-hams@vger.kernel.org
+S:	Maintained
+
+ZF MACHZ WATCHDOG
+P:	Fernando Fuganti
+M:	fuganti@netbank.com.br
+W:	http://cvs.conectiva.com.br/drivers/ZFL-watchdog/
+S:	Maintained
+
+ZR36120 VIDEO FOR LINUX DRIVER
+P:	Pauline Middelink
+M:	middelin@polyware.nl
+W:	http://www.polyware.nl/~middelin/En/hobbies.html
+W:	http://www.polyware.nl/~middelin/hobbies.html
+S:	Maintained
+
+THE REST
+P:	Linus Torvalds
+S:	Buried alive in reporters
diff --git a/kernel/linux2.5/include/linux/if_bridge.h b/kernel/linux2.5/include/linux/if_bridge.h
new file mode 100644
index 0000000..97934c8
--- /dev/null
+++ b/kernel/linux2.5/include/linux/if_bridge.h
@@ -0,0 +1,110 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: if_bridge.h,v 1.1 2002/09/16 19:43:32 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_BRIDGE_H
+#define _LINUX_IF_BRIDGE_H
+
+#include <linux/types.h>
+
+#define BRCTL_VERSION 1
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+#define BRCTL_SET_BRIDGE_HELLO_TIME 9
+#define BRCTL_SET_BRIDGE_MAX_AGE 10
+#define BRCTL_SET_AGEING_TIME 11
+#define BRCTL_SET_GC_INTERVAL 12
+#define BRCTL_GET_PORT_INFO 13
+#define BRCTL_SET_BRIDGE_STP_STATE 14
+#define BRCTL_SET_BRIDGE_PRIORITY 15
+#define BRCTL_SET_PORT_PRIORITY 16
+#define BRCTL_SET_PATH_COST 17
+#define BRCTL_GET_FDB_ENTRIES 18
+
+#define BR_STATE_DISABLED 0
+#define BR_STATE_LISTENING 1
+#define BR_STATE_LEARNING 2
+#define BR_STATE_FORWARDING 3
+#define BR_STATE_BLOCKING 4
+
+struct __bridge_info
+{
+	__u64 designated_root;
+	__u64 bridge_id;
+	__u32 root_path_cost;
+	__u32 max_age;
+	__u32 hello_time;
+	__u32 forward_delay;
+	__u32 bridge_max_age;
+	__u32 bridge_hello_time;
+	__u32 bridge_forward_delay;
+	__u8 topology_change;
+	__u8 topology_change_detected;
+	__u8 root_port;
+	__u8 stp_enabled;
+	__u32 ageing_time;
+	__u32 gc_interval;
+	__u32 hello_timer_value;
+	__u32 tcn_timer_value;
+	__u32 topology_change_timer_value;
+	__u32 gc_timer_value;
+};
+
+struct __port_info
+{
+	__u64 designated_root;
+	__u64 designated_bridge;
+	__u16 port_id;
+	__u16 designated_port;
+	__u32 path_cost;
+	__u32 designated_cost;
+	__u8 state;
+	__u8 top_change_ack;
+	__u8 config_pending;
+	__u8 unused0;
+	__u32 message_age_timer_value;
+	__u32 forward_delay_timer_value;
+	__u32 hold_timer_value;
+};
+
+struct __fdb_entry
+{
+	__u8 mac_addr[6];
+	__u8 port_no;
+	__u8 is_local;
+	__u32 ageing_timer_value;
+	__u32 unused;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/netdevice.h>
+
+struct net_bridge;
+struct net_bridge_port;
+
+extern int (*br_ioctl_hook)(unsigned long arg);
+extern int (*br_handle_frame_hook)(struct sk_buff *skb);
+extern int (*br_should_route_hook)(struct sk_buff **pskb);
+
+#endif
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge.h b/kernel/linux2.5/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..3c271c6
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge.h
@@ -0,0 +1,35 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter. 
+ */
+
+#include <linux/config.h>
+#include <linux/netfilter.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING	4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING		5
+#define NF_BR_NUMHOOKS		6
+
+enum nf_br_hook_priorities {
+	NF_BR_PRI_FIRST = INT_MIN,
+	NF_BR_PRI_FILTER_BRIDGED = -200,
+	NF_BR_PRI_FILTER_OTHER = 200,
+	NF_BR_PRI_NAT_DST_BRIDGED = -300,
+	NF_BR_PRI_NAT_DST_OTHER = 100,
+	NF_BR_PRI_NAT_SRC = 300,
+	NF_BR_PRI_LAST = INT_MAX,
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_802_3.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_802_3.h
new file mode 100644
index 0000000..b9f712c
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_802_3.h
@@ -0,0 +1,69 @@
+#ifndef __LINUX_BRIDGE_EBT_802_3_H
+#define __LINUX_BRIDGE_EBT_802_3_H
+
+#define EBT_802_3_SAP 0x01
+#define EBT_802_3_TYPE 0x02
+
+#define EBT_802_3_MATCH "802_3"
+
+/*
+ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
+ * to discover what kind of packet we're carrying. 
+ */
+#define CHECK_TYPE 0xaa
+
+/*
+ * Control field may be one or two bytes.  If the first byte has
+ * the value 0x03 then the entire length is one byte, otherwise it is two.
+ * One byte controls are used in Unnumbered Information frames.
+ * Two byte controls are used in Numbered Information frames.
+ */
+#define IS_UI 0x03
+
+#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
+
+/* ui has one byte ctrl, ni has two */
+struct hdr_ui {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint8_t ctrl;
+	uint8_t orig[3];
+	uint16_t type;
+};
+
+struct hdr_ni {
+	uint8_t dsap;
+	uint8_t ssap;
+	uint16_t ctrl;
+	uint8_t  orig[3];
+	uint16_t type;
+};
+
+struct ebt_802_3_hdr {
+	uint8_t  daddr[6];
+	uint8_t  saddr[6];
+	uint16_t len;
+	union {
+		struct hdr_ui ui;
+		struct hdr_ni ni;
+	} llc;
+};
+
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct ebt_802_3_hdr *ebt_802_3_hdr(const struct sk_buff *skb)
+{
+	return (struct ebt_802_3_hdr *)skb->mac.raw;
+}
+#endif
+
+struct ebt_802_3_info 
+{
+	uint8_t  sap;
+	uint16_t type;
+	uint8_t  bitmask;
+	uint8_t  invflags;
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_among.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_among.h
new file mode 100644
index 0000000..307c1fe
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_among.h
@@ -0,0 +1,65 @@
+#ifndef __LINUX_BRIDGE_EBT_AMONG_H
+#define __LINUX_BRIDGE_EBT_AMONG_H
+
+#define EBT_AMONG_DST 0x01
+#define EBT_AMONG_SRC 0x02
+
+/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
+ * 
+ * Write-once-read-many hash table, used for checking if a given
+ * MAC address belongs to a set or not and possibly for checking
+ * if it is related with a given IPv4 address.
+ *
+ * The hash value of an address is its last byte.
+ * 
+ * In real-world ethernet addresses, values of the last byte are
+ * evenly distributed and there is no need to consider other bytes.
+ * It would only slow the routines down.
+ *
+ * For MAC address comparison speedup reasons, we introduce a trick.
+ * MAC address is mapped onto an array of two 32-bit integers.
+ * This pair of integers is compared with MAC addresses in the
+ * hash table, which are stored also in form of pairs of integers
+ * (in `cmp' array). This is quick as it requires only two elementary
+ * number comparisons in worst case. Further, we take advantage of
+ * fact that entropy of 3 last bytes of address is larger than entropy
+ * of 3 first bytes. So first we compare 4 last bytes of addresses and
+ * if they are the same we compare 2 first.
+ *
+ * Yes, it is a memory overhead, but in 2003 AD, who cares?
+ */
+
+struct ebt_mac_wormhash_tuple
+{
+	uint32_t cmp[2];
+	uint32_t ip;
+};
+
+struct ebt_mac_wormhash
+{
+	int table[257];
+	int poolsize;
+	struct ebt_mac_wormhash_tuple pool[0];
+};
+
+#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
+		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
+
+struct ebt_among_info
+{
+	int wh_dst_ofs;
+	int wh_src_ofs;
+	int bitmask;
+};
+
+#define EBT_AMONG_DST_NEG 0x1
+#define EBT_AMONG_SRC_NEG 0x2
+
+#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
+#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
+
+#define EBT_AMONG_MATCH "among"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arp.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arp.h
new file mode 100644
index 0000000..537ec6b
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arp.h
@@ -0,0 +1,32 @@
+#ifndef __LINUX_BRIDGE_EBT_ARP_H
+#define __LINUX_BRIDGE_EBT_ARP_H
+
+#define EBT_ARP_OPCODE 0x01
+#define EBT_ARP_HTYPE 0x02
+#define EBT_ARP_PTYPE 0x04
+#define EBT_ARP_SRC_IP 0x08
+#define EBT_ARP_DST_IP 0x10
+#define EBT_ARP_SRC_MAC 0x20
+#define EBT_ARP_DST_MAC 0x40
+#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
+   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
+#define EBT_ARP_MATCH "arp"
+
+struct ebt_arp_info
+{
+	uint16_t htype;
+	uint16_t ptype;
+	uint16_t opcode;
+	uint32_t saddr;
+	uint32_t smsk;
+	uint32_t daddr;
+	uint32_t dmsk;
+	unsigned char smaddr[ETH_ALEN];
+	unsigned char smmsk[ETH_ALEN];
+	unsigned char dmaddr[ETH_ALEN];
+	unsigned char dmmsk[ETH_ALEN];
+	uint8_t  bitmask;
+	uint8_t  invflags;
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arpreply.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arpreply.h
new file mode 100644
index 0000000..96a8339
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_arpreply.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
+#define __LINUX_BRIDGE_EBT_ARPREPLY_H
+
+struct ebt_arpreply_info
+{
+	unsigned char mac[ETH_ALEN];
+	int target;
+};
+#define EBT_ARPREPLY_TARGET "arpreply"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ip.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ip.h
new file mode 100644
index 0000000..7247385
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ip.h
@@ -0,0 +1,43 @@
+/*
+ *  ebt_ip
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP_H
+#define __LINUX_BRIDGE_EBT_IP_H
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#define EBT_IP_PROTO 0x08
+#define EBT_IP_SPORT 0x10
+#define EBT_IP_DPORT 0x20
+#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
+ EBT_IP_SPORT | EBT_IP_DPORT )
+#define EBT_IP_MATCH "ip"
+
+/* the same values are used for the invflags */
+struct ebt_ip_info
+{
+	uint32_t saddr;
+	uint32_t daddr;
+	uint32_t smsk;
+	uint32_t dmsk;
+	uint8_t  tos;
+	uint8_t  protocol;
+	uint8_t  bitmask;
+	uint8_t  invflags;
+	uint16_t sport[2];
+	uint16_t dport[2];
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_limit.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_limit.h
new file mode 100644
index 0000000..d8b6500
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_limit.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
+#define __LINUX_BRIDGE_EBT_LIMIT_H
+
+#define EBT_LIMIT_MATCH "limit"
+
+/* timings are in milliseconds. */
+#define EBT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+struct ebt_limit_info
+{
+	u_int32_t avg;    /* Average secs between packets * scale */
+	u_int32_t burst;  /* Period multiplier for upper limit. */
+
+	/* Used internally by the kernel */
+	unsigned long prev;
+	u_int32_t credit;
+	u_int32_t credit_cap, cost;
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_log.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_log.h
new file mode 100644
index 0000000..358fbc8
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_log.h
@@ -0,0 +1,17 @@
+#ifndef __LINUX_BRIDGE_EBT_LOG_H
+#define __LINUX_BRIDGE_EBT_LOG_H
+
+#define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */
+#define EBT_LOG_ARP 0x02
+#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
+#define EBT_LOG_PREFIX_SIZE 30
+#define EBT_LOG_WATCHER "log"
+
+struct ebt_log_info
+{
+	uint8_t loglevel;
+	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
+	uint32_t bitmask;
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_m.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_m.h
new file mode 100644
index 0000000..301524f
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_m.h
@@ -0,0 +1,15 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
+#define __LINUX_BRIDGE_EBT_MARK_M_H
+
+#define EBT_MARK_AND 0x01
+#define EBT_MARK_OR 0x02
+#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
+struct ebt_mark_m_info
+{
+	unsigned long mark, mask;
+	uint8_t invert;
+	uint8_t bitmask;
+};
+#define EBT_MARK_MATCH "mark_m"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_t.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_t.h
new file mode 100644
index 0000000..110fec6
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_mark_t.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
+#define __LINUX_BRIDGE_EBT_MARK_T_H
+
+struct ebt_mark_t_info
+{
+	unsigned long mark;
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_MARK_TARGET "mark"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_nat.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_nat.h
new file mode 100644
index 0000000..26fd90d
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_nat.h
@@ -0,0 +1,13 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+struct ebt_nat_info
+{
+	unsigned char mac[ETH_ALEN];
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_SNAT_TARGET "snat"
+#define EBT_DNAT_TARGET "dnat"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_pkttype.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_pkttype.h
new file mode 100644
index 0000000..0d64bbb
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_pkttype.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
+#define __LINUX_BRIDGE_EBT_PKTTYPE_H
+
+struct ebt_pkttype_info
+{
+	uint8_t pkt_type;
+	uint8_t invert;
+};
+#define EBT_PKTTYPE_MATCH "pkttype"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_redirect.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_redirect.h
new file mode 100644
index 0000000..5c67990
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_redirect.h
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
+#define __LINUX_BRIDGE_EBT_REDIRECT_H
+
+struct ebt_redirect_info
+{
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_REDIRECT_TARGET "redirect"
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_stp.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_stp.h
new file mode 100644
index 0000000..e5fd678
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_stp.h
@@ -0,0 +1,46 @@
+#ifndef __LINUX_BRIDGE_EBT_STP_H
+#define __LINUX_BRIDGE_EBT_STP_H
+
+#define EBT_STP_TYPE		0x0001
+
+#define EBT_STP_FLAGS		0x0002
+#define EBT_STP_ROOTPRIO	0x0004
+#define EBT_STP_ROOTADDR	0x0008
+#define EBT_STP_ROOTCOST	0x0010
+#define EBT_STP_SENDERPRIO	0x0020
+#define EBT_STP_SENDERADDR	0x0040
+#define EBT_STP_PORT		0x0080
+#define EBT_STP_MSGAGE		0x0100
+#define EBT_STP_MAXAGE		0x0200
+#define EBT_STP_HELLOTIME	0x0400
+#define EBT_STP_FWDD		0x0800
+
+#define EBT_STP_MASK		0x0fff
+#define EBT_STP_CONFIG_MASK	0x0ffe
+
+#define EBT_STP_MATCH "stp"
+
+struct ebt_stp_config_info
+{
+	uint8_t flags;
+	uint16_t root_priol, root_priou;
+	char root_addr[6], root_addrmsk[6];
+	uint32_t root_costl, root_costu;
+	uint16_t sender_priol, sender_priou;
+	char sender_addr[6], sender_addrmsk[6];
+	uint16_t portl, portu;
+	uint16_t msg_agel, msg_ageu;
+	uint16_t max_agel, max_ageu;
+	uint16_t hello_timel, hello_timeu;
+	uint16_t forward_delayl, forward_delayu;
+};
+
+struct ebt_stp_info
+{
+	uint8_t type;
+	struct ebt_stp_config_info config;
+	uint16_t bitmask;
+	uint16_t invflags;
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ulog.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ulog.h
new file mode 100644
index 0000000..b677e26
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_ulog.h
@@ -0,0 +1,36 @@
+#ifndef _EBT_ULOG_H
+#define _EBT_ULOG_H
+
+#define EBT_ULOG_DEFAULT_NLGROUP 0
+#define EBT_ULOG_DEFAULT_QTHRESHOLD 1
+#define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */
+#define EBT_ULOG_PREFIX_LEN 32
+#define EBT_ULOG_MAX_QLEN 50
+#define EBT_ULOG_WATCHER "ulog"
+#define EBT_ULOG_VERSION 1
+
+struct ebt_ulog_info {
+	uint32_t nlgroup;
+	unsigned int cprange;
+	unsigned int qthreshold;
+	char prefix[EBT_ULOG_PREFIX_LEN];
+};
+
+typedef struct ebt_ulog_packet_msg {
+	int version;
+	char indev[IFNAMSIZ];
+	char outdev[IFNAMSIZ];
+	char physindev[IFNAMSIZ];
+	char physoutdev[IFNAMSIZ];
+	char prefix[EBT_ULOG_PREFIX_LEN];
+	struct timeval stamp;
+	unsigned long mark;
+	unsigned int hook;
+	size_t data_len;
+	/* The complete packet, including Ethernet header and perhaps
+	 * the VLAN header is appended */
+	unsigned char data[0] __attribute__
+	                      ((aligned (__alignof__(struct ebt_ulog_info))));
+} ebt_ulog_packet_msg_t;
+
+#endif /* _EBT_ULOG_H */
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebt_vlan.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_vlan.h
new file mode 100644
index 0000000..cb1fcc4
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebt_vlan.h
@@ -0,0 +1,20 @@
+#ifndef __LINUX_BRIDGE_EBT_VLAN_H
+#define __LINUX_BRIDGE_EBT_VLAN_H
+
+#define EBT_VLAN_ID	0x01
+#define EBT_VLAN_PRIO	0x02
+#define EBT_VLAN_ENCAP	0x04
+#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+#define EBT_VLAN_MATCH "vlan"
+
+struct ebt_vlan_info {
+	uint16_t id;		/* VLAN ID {1-4095} */
+	uint8_t prio;		/* VLAN User Priority {0-7} */
+	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
+	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
+				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+				   bit 2=1 - inversed Pirority arg */
+};
+
+#endif
diff --git a/kernel/linux2.5/include/linux/netfilter_bridge/ebtables.h b/kernel/linux2.5/include/linux/netfilter_bridge/ebtables.h
new file mode 100644
index 0000000..b1a7cc9
--- /dev/null
+++ b/kernel/linux2.5/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,363 @@
+/*
+ *  ebtables
+ *
+ *	Authors:
+ *	Bart De Schuymer		<bdschuym@pandora.be>
+ *
+ *  ebtables.c,v 2.0, April, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+/* verdicts >0 are "branches" */
+#define EBT_ACCEPT   -1
+#define EBT_DROP     -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN   -4
+#define NUM_STANDARD_TARGETS   4
+
+struct ebt_counter
+{
+	uint64_t pcnt;
+	uint64_t bcnt;
+};
+
+struct ebt_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	/* total size of the entries */
+	unsigned int entries_size;
+	/* start of the chains */
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+	struct ebt_counter *counters;
+	char *entries;
+};
+
+struct ebt_entries {
+	/* this field is always set to zero
+	 * See EBT_ENTRY_OR_ENTRIES.
+	 * Must be same size as ebt_entry.bitmask */
+	unsigned int distinguisher;
+	/* the chain name */
+	char name[EBT_CHAIN_MAXNAMELEN];
+	/* counter offset for this chain */
+	unsigned int counter_offset;
+	/* one standard (accept, drop, return) per hook */
+	int policy;
+	/* nr. of entries */
+	unsigned int nentries;
+	/* entry list */
+	char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* used for the bitmask of struct ebt_entry */
+
+/* This is a hack to make a difference between an ebt_entry struct and an
+ * ebt_entries struct when traversing the entries from start to end.
+ * Using this simplifies the code alot, while still being able to use
+ * ebt_entries.
+ * Contrary, iptables doesn't use something like ebt_entries and therefore uses
+ * different techniques for naming the policy and such. So, iptables doesn't
+ * need a hack like this.
+ */
+#define EBT_ENTRY_OR_ENTRIES 0x01
+/* these are the normal masks */
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+   | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_match *match;
+	} u;
+	/* size of data */
+	unsigned int match_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_watcher
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_watcher *watcher;
+	} u;
+	/* size of data */
+	unsigned int watcher_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_target
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_target *target;
+	} u;
+	/* size of data */
+	unsigned int target_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+	struct ebt_entry_target target;
+	int verdict;
+};
+
+/* one entry */
+struct ebt_entry {
+	/* this needs to be the first field */
+	unsigned int bitmask;
+	unsigned int invflags;
+	uint16_t ethproto;
+	/* the physical in-dev */
+	char in[IFNAMSIZ];
+	/* the logical in-dev */
+	char logical_in[IFNAMSIZ];
+	/* the physical out-dev */
+	char out[IFNAMSIZ];
+	/* the logical out-dev */
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	/* sizeof ebt_entry + matches */
+	unsigned int watchers_offset;
+	/* sizeof ebt_entry + matches + watchers */
+	unsigned int target_offset;
+	/* sizeof ebt_entry + matches + watchers + target */
+	unsigned int next_offset;
+	unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* {g,s}etsockopt numbers */
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+#ifdef __KERNEL__
+
+/* return values for match() functions */
+#define EBT_MATCH 0
+#define EBT_NOMATCH 1
+
+struct ebt_match
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	/* 0 == it matches */
+	int (*match)(const struct sk_buff *skb, const struct net_device *in,
+	   const struct net_device *out, const void *matchdata,
+	   unsigned int datalen);
+	/* 0 == let it in */
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+	void (*destroy)(void *matchdata, unsigned int datalen);
+	struct module *me;
+};
+
+struct ebt_watcher
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	void (*watcher)(const struct sk_buff *skb, unsigned int hooknr,
+	   const struct net_device *in, const struct net_device *out,
+	   const void *watcherdata, unsigned int datalen);
+	/* 0 == let it in */
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+	void (*destroy)(void *watcherdata, unsigned int datalen);
+	struct module *me;
+};
+
+struct ebt_target
+{
+	struct list_head list;
+	const char name[EBT_FUNCTION_MAXNAMELEN];
+	/* returns one of the standard verdicts */
+	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
+	   const struct net_device *in, const struct net_device *out,
+	   const void *targetdata, unsigned int datalen);
+	/* 0 == let it in */
+	int (*check)(const char *tablename, unsigned int hookmask,
+	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+	void (*destroy)(void *targetdata, unsigned int datalen);
+	struct module *me;
+};
+
+/* used for jumping from and into user defined chains (udc) */
+struct ebt_chainstack
+{
+	struct ebt_entries *chaininfo; /* pointer to chain data */
+	struct ebt_entry *e; /* pointer to entry data */
+	unsigned int n; /* n'th entry */
+};
+
+struct ebt_table_info
+{
+	/* total size of the entries */
+	unsigned int entries_size;
+	unsigned int nentries;
+	/* pointers to the start of the chains */
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	/* room to maintain the stack used for jumping from and into udc */
+	struct ebt_chainstack **chainstack;
+	char *entries;
+	struct ebt_counter counters[0] ____cacheline_aligned;
+};
+
+struct ebt_table
+{
+	struct list_head list;
+	char name[EBT_TABLE_MAXNAMELEN];
+	struct ebt_replace *table;
+	unsigned int valid_hooks;
+	rwlock_t lock;
+	/* e.g. could be the table explicitly only allows certain
+	 * matches, targets, ... 0 == let it in */
+	int (*check)(const struct ebt_table_info *info,
+	   unsigned int valid_hooks);
+	/* the data used by the kernel */
+	struct ebt_table_info *private;
+	struct module *me;
+};
+
+#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_replace)-1)) & \
+		     ~(__alignof__(struct ebt_replace)-1))
+extern int ebt_register_table(struct ebt_table *table);
+extern void ebt_unregister_table(struct ebt_table *table);
+extern int ebt_register_match(struct ebt_match *match);
+extern void ebt_unregister_match(struct ebt_match *match);
+extern int ebt_register_watcher(struct ebt_watcher *watcher);
+extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
+extern int ebt_register_target(struct ebt_target *target);
+extern void ebt_unregister_target(struct ebt_target *target);
+extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   struct ebt_table *table);
+
+/* Used in the kernel match() functions */
+#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
+/* True if the hook mask denotes that the rule is in a base chain,
+ * used in the check() functions */
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+/* Clear the bit in the hook mask that tells if the rule is on a base chain */
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+/* True if the target is not a standard target */
+#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
+
+#endif /* __KERNEL__ */
+
+/* blatently stolen from ip_tables.h
+ * fn returns 0 to continue iteration */
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_match *__match;                    \
+	                                                    \
+	for (__i = sizeof(struct ebt_entry);                \
+	     __i < (e)->watchers_offset;                    \
+	     __i += __match->match_size +                   \
+	     sizeof(struct ebt_entry_match)) {              \
+		__match = (void *)(e) + __i;                \
+		                                            \
+		__ret = fn(__match , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->watchers_offset)            \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_watcher *__watcher;                \
+	                                                    \
+	for (__i = e->watchers_offset;                      \
+	     __i < (e)->target_offset;                      \
+	     __i += __watcher->watcher_size +               \
+	     sizeof(struct ebt_entry_watcher)) {            \
+		__watcher = (void *)(e) + __i;              \
+		                                            \
+		__ret = fn(__watcher , ## args);            \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->target_offset)              \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry *__entry;                          \
+	                                                    \
+	for (__i = 0; __i < (size);) {                      \
+		__entry = (void *)(entries) + __i;          \
+		__ret = fn(__entry , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+		if (__entry->bitmask != 0)                  \
+			__i += __entry->next_offset;        \
+		else                                        \
+			__i += sizeof(struct ebt_entries);  \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (size))                          \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#endif
diff --git a/kernel/linux2.5/net/Config.in b/kernel/linux2.5/net/Config.in
new file mode 100644
index 0000000..47a086a
--- /dev/null
+++ b/kernel/linux2.5/net/Config.in
@@ -0,0 +1,96 @@
+#
+# Network configuration
+#
+mainmenu_option next_comment
+comment 'Networking options'
+tristate 'Packet socket' CONFIG_PACKET
+if [ "$CONFIG_PACKET" != "n" ]; then
+   bool '  Packet socket: mmapped IO' CONFIG_PACKET_MMAP
+fi
+
+tristate 'Netlink device emulation' CONFIG_NETLINK_DEV
+
+bool 'Network packet filtering (replaces ipchains)' CONFIG_NETFILTER
+if [ "$CONFIG_NETFILTER" = "y" ]; then
+   bool '  Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
+fi
+bool 'Socket Filtering'  CONFIG_FILTER
+tristate 'Unix domain sockets' CONFIG_UNIX
+bool 'TCP/IP networking' CONFIG_INET
+if [ "$CONFIG_INET" = "y" ]; then
+   source net/ipv4/Config.in
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+#   IPv6 as module will cause a CRASH if you try to unload it
+      tristate '  The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
+      if [ "$CONFIG_IPV6" != "n" ]; then
+	 source net/ipv6/Config.in
+      fi
+   fi
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      source net/sctp/Config.in
+   fi
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
+   if [ "$CONFIG_ATM" = "y" ]; then
+      if [ "$CONFIG_INET" = "y" ]; then
+	 bool '  Classical IP over ATM (EXPERIMENTAL)' CONFIG_ATM_CLIP
+	 if [ "$CONFIG_ATM_CLIP" = "y" ]; then
+	    bool '    Do NOT send ICMP if no neighbour (EXPERIMENTAL)' CONFIG_ATM_CLIP_NO_ICMP
+	 fi
+      fi
+      tristate '  LAN Emulation (LANE) support (EXPERIMENTAL)' CONFIG_ATM_LANE
+      if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then
+	 tristate '    Multi-Protocol Over ATM (MPOA) support (EXPERIMENTAL)' CONFIG_ATM_MPOA
+      fi
+   fi
+fi
+tristate '802.1Q VLAN Support' CONFIG_VLAN_8021Q
+
+tristate 'ANSI/IEEE 802.2 Data link layer protocol' CONFIG_LLC
+if [ "$CONFIG_LLC" != "n" ]; then
+   tristate '  LLC sockets interface' CONFIG_LLC_UI
+fi
+
+dep_tristate 'The IPX protocol' CONFIG_IPX $CONFIG_LLC
+if [ "$CONFIG_IPX" != "n" ]; then
+   source net/ipx/Config.in
+fi
+
+dep_tristate 'Appletalk protocol support' CONFIG_ATALK $CONFIG_LLC
+source drivers/net/appletalk/Config.in
+
+tristate 'DECnet Support' CONFIG_DECNET
+if [ "$CONFIG_DECNET" != "n" ]; then
+   source net/decnet/Config.in
+fi
+dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
+if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+   source net/bridge/netfilter/Config.in
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+   tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+   bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
+   if [ "$CONFIG_INET" = "y" ]; then
+      tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
+      if [ "$CONFIG_ECONET" != "n" ]; then
+	 bool '  AUN over UDP' CONFIG_ECONET_AUNUDP
+	 bool '  Native Econet' CONFIG_ECONET_NATIVE
+      fi
+   fi
+   tristate 'WAN router' CONFIG_WAN_ROUTER
+   bool 'Fast switching (read help!)' CONFIG_NET_FASTROUTE
+   bool 'Forwarding between high speed interfaces' CONFIG_NET_HW_FLOWCONTROL
+fi
+
+mainmenu_option next_comment
+comment 'QoS and/or fair queueing'
+bool 'QoS and/or fair queueing' CONFIG_NET_SCHED
+if [ "$CONFIG_NET_SCHED" = "y" ]; then
+   source net/sched/Config.in
+fi
+#bool 'Network code profiler' CONFIG_NET_PROFILE
+endmenu
+
+endmenu
diff --git a/kernel/linux2.5/net/Makefile b/kernel/linux2.5/net/Makefile
new file mode 100644
index 0000000..4fed0f5
--- /dev/null
+++ b/kernel/linux2.5/net/Makefile
@@ -0,0 +1,45 @@
+#
+# Makefile for the linux networking.
+#
+# 2 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+O_TARGET :=	network.o
+
+
+export-objs :=	netsyms.o
+
+obj-y	:= socket.o core/
+
+obj-$(CONFIG_NET)		+= ethernet/ 802/ sched/ netlink/
+obj-$(CONFIG_INET)		+= ipv4/
+obj-$(CONFIG_UNIX)		+= unix/
+obj-$(CONFIG_IPV6)		+= ipv6/
+obj-$(CONFIG_PACKET)		+= packet/
+obj-$(CONFIG_NET_SCHED)		+= sched/
+obj-$(CONFIG_BRIDGE)		+= bridge/
+obj-$(CONFIG_IPX)		+= ipx/
+obj-$(CONFIG_ATALK)		+= appletalk/
+obj-$(CONFIG_WAN_ROUTER)	+= wanrouter/
+obj-$(CONFIG_X25)		+= x25/
+obj-$(CONFIG_LAPB)		+= lapb/
+obj-$(CONFIG_NETROM)		+= netrom/
+obj-$(CONFIG_ROSE)		+= rose/
+obj-$(CONFIG_AX25)		+= ax25/
+obj-$(CONFIG_IRDA)		+= irda/
+obj-$(CONFIG_BLUEZ)		+= bluetooth/
+obj-$(CONFIG_SUNRPC)		+= sunrpc/
+obj-$(CONFIG_ATM)		+= atm/
+obj-$(CONFIG_DECNET)		+= decnet/
+obj-$(CONFIG_ECONET)		+= econet/
+obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
+obj-$(CONFIG_LLC)		+= llc/
+obj-$(CONFIG_IP_SCTP)		+= sctp/
+
+ifeq ($(CONFIG_NET),y)
+obj-$(CONFIG_MODULES)		+= netsyms.o
+obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/kernel/linux2.5/net/bridge/Makefile b/kernel/linux2.5/net/bridge/Makefile
new file mode 100644
index 0000000..31a9d86
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the IEEE 802.1d ethernet bridging layer.
+#
+
+export-objs := br.o
+
+obj-$(CONFIG_BRIDGE) += bridge.o
+
+bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+			br_stp_if.o br_stp_timer.o
+obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+
+include $(TOPDIR)/Rules.make
diff --git a/kernel/linux2.5/net/bridge/br.c b/kernel/linux2.5/net/bridge/br.c
new file mode 100644
index 0000000..1222e3d
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/br.c
@@ -0,0 +1,83 @@
+/*
+ *	Generic parts
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br.c,v 1.2 2002/09/16 19:50:06 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/if_bridge.h>
+#include <asm/uaccess.h>
+#include "br_private.h"
+
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+#include "../atm/lec.h"
+#endif
+
+int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+
+void br_dec_use_count()
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void br_inc_use_count()
+{
+	MOD_INC_USE_COUNT;
+}
+
+static int __init br_init(void)
+{
+	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+
+	br_handle_frame_hook = br_handle_frame;
+	br_ioctl_hook = br_ioctl_deviceless_stub;
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = br_fdb_get;
+	br_fdb_put_hook = br_fdb_put;
+#endif
+	register_netdevice_notifier(&br_device_notifier);
+
+	return 0;
+}
+
+static void __br_clear_frame_hook(void)
+{
+	br_handle_frame_hook = NULL;
+}
+
+static void __br_clear_ioctl_hook(void)
+{
+	br_ioctl_hook = NULL;
+}
+
+static void __exit br_deinit(void)
+{
+	unregister_netdevice_notifier(&br_device_notifier);
+	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+	net_call_rx_atomic(__br_clear_frame_hook);
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	br_fdb_get_hook = NULL;
+	br_fdb_put_hook = NULL;
+#endif
+}
+
+EXPORT_SYMBOL(br_should_route_hook);
+
+module_init(br_init)
+module_exit(br_deinit)
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/br_forward.c b/kernel/linux2.5/net/bridge/br_forward.c
new file mode 100644
index 0000000..d391a4b
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/br_forward.c
@@ -0,0 +1,151 @@
+/*
+ *	Forwarding decision
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_forward.c,v 1.1 2002/09/10 17:46:06 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb)
+{
+	if (skb->dev == p->dev ||
+	    p->state != BR_STATE_FORWARDING)
+		return 0;
+
+	return 1;
+}
+
+static int __dev_queue_push_xmit(struct sk_buff *skb)
+{
+	skb_push(skb, ETH_HLEN);
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+static int __br_forward_finish(struct sk_buff *skb)
+{
+	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+			__dev_queue_push_xmit);
+
+	return 0;
+}
+
+static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	skb->dev = to->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+			__br_forward_finish);
+}
+
+static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	indev = skb->dev;
+	skb->dev = to->dev;
+
+	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+			__br_forward_finish);
+}
+
+/* called under bridge lock */
+void br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_deliver(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+{
+	if (should_deliver(to, skb)) {
+		__br_forward(to, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
+	void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb))
+{
+	struct net_bridge_port *p;
+	struct net_bridge_port *prev;
+
+	if (clone) {
+		struct sk_buff *skb2;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			br->statistics.tx_dropped++;
+			return;
+		}
+
+		skb = skb2;
+	}
+
+	prev = NULL;
+
+	p = br->port_list;
+	while (p != NULL) {
+		if (should_deliver(p, skb)) {
+			if (prev != NULL) {
+				struct sk_buff *skb2;
+
+				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+					br->statistics.tx_dropped++;
+					kfree_skb(skb);
+					return;
+				}
+
+				__packet_hook(prev, skb2);
+			}
+
+			prev = p;
+		}
+
+		p = p->next;
+	}
+
+	if (prev != NULL) {
+		__packet_hook(prev, skb);
+		return;
+	}
+
+	kfree_skb(skb);
+}
+
+/* called under bridge lock */
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_deliver);
+}
+
+/* called under bridge lock */
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+{
+	br_flood(br, skb, clone, __br_forward);
+}
diff --git a/kernel/linux2.5/net/bridge/br_input.c b/kernel/linux2.5/net/bridge/br_input.c
new file mode 100644
index 0000000..b551775
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/br_input.c
@@ -0,0 +1,179 @@
+/*
+ *	Handle incoming frames
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_input.c,v 1.4 2002/09/29 18:25:11 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/netfilter_bridge.h>
+#include "br_private.h"
+
+unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+static int br_pass_frame_up_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+	skb->nf_debug = 0;
+#endif
+	netif_rx(skb);
+
+	return 0;
+}
+
+static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
+{
+	struct net_device *indev;
+
+	br->statistics.rx_packets++;
+	br->statistics.rx_bytes += skb->len;
+
+	indev = skb->dev;
+	skb->dev = &br->dev;
+	skb->pkt_type = PACKET_HOST;
+	skb_push(skb, ETH_HLEN);
+	skb->protocol = eth_type_trans(skb, &br->dev);
+
+	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
+			br_pass_frame_up_finish);
+}
+
+static int br_handle_frame_finish(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_fdb_entry *dst;
+	struct net_bridge_port *p;
+	int passedup;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	passedup = 0;
+	if (br->dev.flags & IFF_PROMISC) {
+		struct sk_buff *skb2;
+
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 != NULL) {
+			passedup = 1;
+			br_pass_frame_up(br, skb2);
+		}
+	}
+
+	if (dest[0] & 1) {
+		br_flood_forward(br, skb, !passedup);
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		goto out;
+	}
+
+	dst = br_fdb_get(br, dest);
+	if (dst != NULL && dst->is_local) {
+		if (!passedup)
+			br_pass_frame_up(br, skb);
+		else
+			kfree_skb(skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	if (dst != NULL) {
+		br_forward(dst->dst, skb);
+		br_fdb_put(dst);
+		goto out;
+	}
+
+	br_flood_forward(br, skb, 0);
+
+out:
+	read_unlock(&br->lock);
+	return 0;
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+}
+
+int br_handle_frame(struct sk_buff *skb)
+{
+	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_port *p;
+
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
+
+	if (!(br->dev.flags & IFF_UP) ||
+	    p->state == BR_STATE_DISABLED)
+		goto err;
+
+	if (skb->mac.ethernet->h_source[0] & 1)
+		goto err;
+
+	if (p->state == BR_STATE_LEARNING ||
+	    p->state == BR_STATE_FORWARDING)
+		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+	if (br->stp_enabled &&
+	    !memcmp(dest, bridge_ula, 5) &&
+	    !(dest[5] & 0xF0))
+		goto handle_special_frame;
+
+	if (p->state == BR_STATE_FORWARDING) {
+		if (br_should_route_hook && br_should_route_hook(&skb)) {
+			read_unlock(&br->lock);
+			return -1;
+		}
+
+		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+			br_handle_frame_finish);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return 0;
+
+handle_special_frame:
+	if (!dest[5]) {
+		br_stp_handle_bpdu(skb);
+		read_unlock(&br->lock);
+		return 0;
+	}
+
+	kfree_skb(skb);
+	read_unlock(&br->lock);
+	return 0;
+}
diff --git a/kernel/linux2.5/net/bridge/br_private.h b/kernel/linux2.5/net/bridge/br_private.h
new file mode 100644
index 0000000..124067e
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/br_private.h
@@ -0,0 +1,204 @@
+/*
+ *	Linux ethernet bridge
+ *
+ *	Authors:
+ *	Lennert Buytenhek		<buytenh@gnu.org>
+ *
+ *	$Id: br_private.h,v 1.1 2002/08/23 20:04:31 bdschuym Exp $
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	as published by the Free Software Foundation; either version
+ *	2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _BR_PRIVATE_H
+#define _BR_PRIVATE_H
+
+#include <linux/netdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/if_bridge.h>
+#include "br_private_timer.h"
+
+#define BR_HASH_BITS 8
+#define BR_HASH_SIZE (1 << BR_HASH_BITS)
+
+#define BR_HOLD_TIME (1*HZ)
+
+typedef struct bridge_id bridge_id;
+typedef struct mac_addr mac_addr;
+typedef __u16 port_id;
+
+struct bridge_id
+{
+	unsigned char	prio[2];
+	unsigned char	addr[6];
+};
+
+struct mac_addr
+{
+	unsigned char	addr[6];
+	unsigned char	pad[2];
+};
+
+struct net_bridge_fdb_entry
+{
+	struct net_bridge_fdb_entry	*next_hash;
+	struct net_bridge_fdb_entry	**pprev_hash;
+	atomic_t			use_count;
+	mac_addr			addr;
+	struct net_bridge_port		*dst;
+	unsigned long			ageing_timer;
+	unsigned			is_local:1;
+	unsigned			is_static:1;
+};
+
+struct net_bridge_port
+{
+	struct net_bridge_port		*next;
+	struct net_bridge		*br;
+	struct net_device		*dev;
+	int				port_no;
+
+	/* STP */
+	port_id				port_id;
+	int				state;
+	int				path_cost;
+	bridge_id			designated_root;
+	int				designated_cost;
+	bridge_id			designated_bridge;
+	port_id				designated_port;
+	unsigned			topology_change_ack:1;
+	unsigned			config_pending:1;
+	int				priority;
+
+	struct br_timer			forward_delay_timer;
+	struct br_timer			hold_timer;
+	struct br_timer			message_age_timer;
+};
+
+struct net_bridge
+{
+	struct net_bridge		*next;
+	rwlock_t			lock;
+	struct net_bridge_port		*port_list;
+	struct net_device		dev;
+	struct net_device_stats		statistics;
+	rwlock_t			hash_lock;
+	struct net_bridge_fdb_entry	*hash[BR_HASH_SIZE];
+	struct timer_list		tick;
+
+	/* STP */
+	bridge_id			designated_root;
+	int				root_path_cost;
+	int				root_port;
+	int				max_age;
+	int				hello_time;
+	int				forward_delay;
+	bridge_id			bridge_id;
+	int				bridge_max_age;
+	int				bridge_hello_time;
+	int				bridge_forward_delay;
+	unsigned			stp_enabled:1;
+	unsigned			topology_change:1;
+	unsigned			topology_change_detected:1;
+
+	struct br_timer			hello_timer;
+	struct br_timer			tcn_timer;
+	struct br_timer			topology_change_timer;
+	struct br_timer			gc_timer;
+
+	int				ageing_time;
+	int				gc_interval;
+};
+
+extern struct notifier_block br_device_notifier;
+extern unsigned char bridge_ula[6];
+
+/* br.c */
+extern void br_dec_use_count(void);
+extern void br_inc_use_count(void);
+
+/* br_device.c */
+extern void br_dev_setup(struct net_device *dev);
+extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+
+/* br_fdb.c */
+extern void br_fdb_changeaddr(struct net_bridge_port *p,
+		       unsigned char *newaddr);
+extern void br_fdb_cleanup(struct net_bridge *br);
+extern void br_fdb_delete_by_port(struct net_bridge *br,
+			   struct net_bridge_port *p);
+extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
+					unsigned char *addr);
+extern void br_fdb_put(struct net_bridge_fdb_entry *ent);
+extern int  br_fdb_get_entries(struct net_bridge *br,
+			unsigned char *_buf,
+			int maxnum,
+			int offset);
+extern void br_fdb_insert(struct net_bridge *br,
+		   struct net_bridge_port *source,
+		   unsigned char *addr,
+		   int is_local);
+
+/* br_forward.c */
+extern void br_deliver(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern void br_forward(struct net_bridge_port *to,
+		struct sk_buff *skb);
+extern void br_flood_deliver(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+extern void br_flood_forward(struct net_bridge *br,
+		      struct sk_buff *skb,
+		      int clone);
+
+/* br_if.c */
+extern int br_add_bridge(char *name);
+extern int br_del_bridge(char *name);
+extern int br_add_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_del_if(struct net_bridge *br,
+	      struct net_device *dev);
+extern int br_get_bridge_ifindices(int *indices,
+			    int num);
+extern void br_get_port_ifindices(struct net_bridge *br,
+			   int *ifindices);
+
+/* br_input.c */
+extern int br_handle_frame(struct sk_buff *skb);
+
+/* br_ioctl.c */
+extern void br_call_ioctl_atomic(void (*fn)(void));
+extern int br_ioctl(struct net_bridge *br,
+	     unsigned int cmd,
+	     unsigned long arg0,
+	     unsigned long arg1,
+	     unsigned long arg2);
+extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_stp.c */
+extern int br_is_root_bridge(struct net_bridge *br);
+extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+				    int port_no);
+extern void br_init_port(struct net_bridge_port *p);
+extern port_id br_make_port_id(struct net_bridge_port *p);
+extern void br_become_designated_port(struct net_bridge_port *p);
+
+/* br_stp_if.c */
+extern void br_stp_enable_bridge(struct net_bridge *br);
+extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_enable_port(struct net_bridge_port *p);
+extern void br_stp_disable_port(struct net_bridge_port *p);
+extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
+extern void br_stp_set_bridge_priority(struct net_bridge *br,
+				int newprio);
+extern void br_stp_set_port_priority(struct net_bridge_port *p,
+			      int newprio);
+extern void br_stp_set_path_cost(struct net_bridge_port *p,
+			  int path_cost);
+
+/* br_stp_bpdu.c */
+extern void br_stp_handle_bpdu(struct sk_buff *skb);
+
+#endif
diff --git a/kernel/linux2.5/net/bridge/netfilter/Kconfig b/kernel/linux2.5/net/bridge/netfilter/Kconfig
new file mode 100644
index 0000000..36de8e1
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/Kconfig
@@ -0,0 +1,155 @@
+#
+# Bridge netfilter configuration
+#
+config BRIDGE_NF_EBTABLES
+	tristate "Bridge: ebtables"
+	depends on NETFILTER && BRIDGE
+
+config BRIDGE_EBT_BROUTE
+	tristate "ebt: broute table support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  The ebtables broute table is used to define rules that decide between
+	  bridging and routing frames, giving Linux the functionality of a
+	  brouter. See the man page for ebtables(8) and examples on the ebtables
+	  website.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_T_FILTER
+	tristate "ebt: filter table support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  The ebtables filter table is used to define frame filtering rules at
+	  local input, forwarding and local output. See the man page for
+	  ebtables(8).
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_T_NAT
+	tristate "ebt: nat table support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  The ebtables nat table is used to define rules that alter the MAC
+	  source address (MAC SNAT) or the MAC destination address (MAC DNAT).
+	  See the man page for ebtables(8).
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_802_3
+	tristate "ebt: 802.3 filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds matching support for 802.3 Ethernet frames.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_ARP
+	tristate "ebt: ARP filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the ARP match, which allows ARP and RARP header field
+	  filtering.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_IP
+	tristate "ebt: IP filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the IP match, which allows basic IP header field
+	  filtering.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_LOG
+	tristate "ebt: log support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the log target, that you can use in any rule in
+	  any ebtables table. It records the frame header to the syslog.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_MARK
+	tristate "ebt: mark filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the mark match, which allows matching frames based on
+	  the 'nfmark' value in the frame. This can be set by the mark target.
+	  This value is the same as the one used in the iptables mark match and
+	  target.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_PKTTYPE
+	tristate "ebt: packet type filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the packet type match, which allows matching on the
+	  type of packet based on its Ethernet "class" (as determined by
+	  the generic networking code): broadcast, multicast,
+	  for this host alone or for another host.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_VLAN
+	tristate "ebt: 802.1Q VLAN filter support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the 802.1Q vlan match, which allows the filtering of
+	  802.1Q vlan fields.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_DNAT
+	tristate "ebt: dnat target support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the MAC DNAT target, which allows altering the MAC
+	  destination address of frames.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_MARK_T
+	tristate "ebt: mark target support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the mark target, which allows marking frames by
+	  setting the 'nfmark' value in the frame.
+	  This value is the same as the one used in the iptables mark match and
+	  target.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_REDIRECT
+	tristate "ebt: redirect target support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the MAC redirect target, which allows altering the MAC
+	  destination address of a frame to that of the device it arrived on.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
+
+config BRIDGE_EBT_SNAT
+	tristate "ebt: snat target support"
+	depends on BRIDGE_NF_EBTABLES
+	help
+	  This option adds the MAC SNAT target, which allows altering the MAC
+	  source address of frames.
+
+	  If you want to compile it as a module, say M here and read
+	  <file:Documentation/modules.txt>.  If unsure, say `N'.
diff --git a/kernel/linux2.5/net/bridge/netfilter/Makefile b/kernel/linux2.5/net/bridge/netfilter/Makefile
new file mode 100644
index 0000000..3fac8e3
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the netfilter modules for Link Layer filtering on a bridge.
+#
+
+obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
+obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+
+obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
+obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o
+obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o
+obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o
+obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
+obj-$(CONFIG_BRIDGE_EBT_VLAN) += ebt_vlan.o
+
+obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+
+obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_802_3.c b/kernel/linux2.5/net/bridge/netfilter/ebt_802_3.c
new file mode 100644
index 0000000..1db4434
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_802_3.c
@@ -0,0 +1,73 @@
+/*
+ * 802_3
+ *
+ * Author:
+ * Chris Vitale csv@bluetail.com
+ *
+ * May 2003
+ * 
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_802_3.h>
+#include <linux/module.h>
+
+static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
+	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
+
+	if (info->bitmask & EBT_802_3_SAP) {
+		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
+				return EBT_NOMATCH;
+		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
+				return EBT_NOMATCH;
+	}
+
+	if (info->bitmask & EBT_802_3_TYPE) {
+		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
+			return EBT_NOMATCH;
+		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
+			return EBT_NOMATCH;
+	}
+
+	return EBT_MATCH;
+}
+
+static struct ebt_match filter_802_3;
+static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
+
+	if (datalen < sizeof(struct ebt_802_3_info))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct ebt_match filter_802_3 =
+{
+	.name		= EBT_802_3_MATCH,
+	.match		= ebt_filter_802_3,
+	.check		= ebt_802_3_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_802_3);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_802_3);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_among.c b/kernel/linux2.5/net/bridge/netfilter/ebt_among.c
new file mode 100644
index 0000000..181788e
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_among.c
@@ -0,0 +1,215 @@
+/*
+ *  ebt_among
+ *
+ *	Authors:
+ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ *  August, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+
+static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
+				     const char *mac, uint32_t ip)
+{
+	/* You may be puzzled as to how this code works.
+	 * Some tricks were used, refer to 
+	 * 	include/linux/netfilter_bridge/ebt_among.h
+	 * as there you can find a solution of this mystery.
+	 */
+	const struct ebt_mac_wormhash_tuple *p;
+	int start, limit, i;
+	uint32_t cmp[2] = { 0, 0 };
+	int key = (const unsigned char) mac[5];
+
+	memcpy(((char *) cmp) + 2, mac, 6);
+	start = wh->table[key];
+	limit = wh->table[key + 1];
+	if (ip) {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0 || p->ip == ip) {
+					return 1;
+				}
+			}
+		}
+	} else {
+		for (i = start; i < limit; i++) {
+			p = &wh->pool[i];
+			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
+				if (p->ip == 0) {
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
+					    *wh)
+{
+	int i;
+
+	for (i = 0; i < 256; i++) {
+		if (wh->table[i] > wh->table[i + 1])
+			return -0x100 - i;
+		if (wh->table[i] < 0)
+			return -0x200 - i;
+		if (wh->table[i] > wh->poolsize)
+			return -0x300 - i;
+	}
+	if (wh->table[256] > wh->poolsize)
+		return -0xc00;
+	return 0;
+}
+
+static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		struct iphdr iph;
+
+		if (skb_copy_bits(skb, 0, &iph, sizeof(iph)))
+			return -1;
+		*addr = iph.daddr;
+	} else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		struct arphdr arph;
+
+		if (skb_copy_bits(skb, 0, &arph, sizeof(arph)) ||
+		    arph.ar_pln != sizeof(uint32_t) || arph.ar_hln != ETH_ALEN)
+			return -1;
+		if (skb_copy_bits(skb, sizeof(struct arphdr) +
+		    2 * ETH_ALEN + sizeof(uint32_t), addr, sizeof(uint32_t)))
+			return -1;
+	}
+	return 0;
+}
+
+static int get_ip_src(const struct sk_buff *skb, uint32_t *addr)
+{
+	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) {
+		struct iphdr iph;
+
+		if (skb_copy_bits(skb, 0, &iph, sizeof(iph)))
+			return -1;
+		*addr = iph.saddr;
+	} else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
+		struct arphdr arph;
+
+		if (skb_copy_bits(skb, 0, &arph, sizeof(arph)) ||
+		    arph.ar_pln != sizeof(uint32_t) || arph.ar_hln != ETH_ALEN)
+			return -1;
+		if (skb_copy_bits(skb, sizeof(struct arphdr) +
+		    ETH_ALEN, addr, sizeof(uint32_t)))
+			return -1;
+	}
+	return 0;
+}
+
+static int ebt_filter_among(const struct sk_buff *skb,
+			    const struct net_device *in,
+			    const struct net_device *out, const void *data,
+			    unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	const char *dmac, *smac;
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	uint32_t dip = 0, sip = 0;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+
+	if (wh_src) {
+		smac = skb->mac.ethernet->h_source;
+		if (get_ip_src(skb, &sip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	if (wh_dst) {
+		dmac = skb->mac.ethernet->h_dest;
+		if (get_ip_dst(skb, &dip))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
+			/* we match only if it contains */
+			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		} else {
+			/* we match only if it DOES NOT contain */
+			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
+				return EBT_NOMATCH;
+		}
+	}
+
+	return EBT_MATCH;
+}
+
+static int ebt_among_check(const char *tablename, unsigned int hookmask,
+			   const struct ebt_entry *e, void *data,
+			   unsigned int datalen)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *) data;
+	int expected_length = sizeof(struct ebt_among_info);
+	const struct ebt_mac_wormhash *wh_dst, *wh_src;
+	int err;
+
+	wh_dst = ebt_among_wh_dst(info);
+	wh_src = ebt_among_wh_src(info);
+	expected_length += ebt_mac_wormhash_size(wh_dst);
+	expected_length += ebt_mac_wormhash_size(wh_src);
+
+	if (datalen != EBT_ALIGN(expected_length)) {
+		printk(KERN_WARNING
+		       "ebtables: among: wrong size: %d"
+		       "against expected %d, rounded to %d\n",
+		       datalen, expected_length,
+		       EBT_ALIGN(expected_length));
+		return -EINVAL;
+	}
+	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
+		printk(KERN_WARNING
+		       "ebtables: among: dst integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
+		printk(KERN_WARNING
+		       "ebtables: among: src integrity fail: %x\n", -err);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct ebt_match filter_among = {
+	.name		= EBT_AMONG_MATCH, 
+	.match		= ebt_filter_among, 
+	.check		= ebt_among_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_among);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_among);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_arp.c b/kernel/linux2.5/net/bridge/netfilter/ebt_arp.c
new file mode 100644
index 0000000..d63d720
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_arp.c
@@ -0,0 +1,133 @@
+/*
+ *  ebt_arp
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *	Tim Gardner <timg@tpi.com>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/module.h>
+
+static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+	struct arphdr arph;
+
+	if (skb_copy_bits(skb, 0, &arph, sizeof(arph)))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+	   arph.ar_op, EBT_ARP_OPCODE))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+	   arph.ar_hrd, EBT_ARP_HTYPE))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+	   arph.ar_pro, EBT_ARP_PTYPE))
+		return EBT_NOMATCH;
+
+	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
+		uint32_t addr;
+
+		/* IPv4 addresses are always 4 bytes */
+		if (arph.ar_pln != sizeof(uint32_t))
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_SRC_IP) {
+			if (skb_copy_bits(skb, sizeof(struct arphdr) +
+			    arph.ar_hln, &addr, sizeof(addr)))
+				return EBT_NOMATCH;
+			if (FWINV(info->saddr != (addr & info->smsk),
+			   EBT_ARP_SRC_IP))
+				return EBT_NOMATCH;
+		}
+
+		if (info->bitmask & EBT_ARP_DST_IP) {
+			if (skb_copy_bits(skb, sizeof(struct arphdr) +
+			    2*arph.ar_hln + sizeof(uint32_t), &addr,
+			    sizeof(addr)))
+				return EBT_NOMATCH;
+			if (FWINV(info->daddr != (addr & info->dmsk),
+			   EBT_ARP_DST_IP))
+				return EBT_NOMATCH;
+		}
+	}
+
+	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
+		unsigned char mac[ETH_ALEN];
+		uint8_t verdict, i;
+
+		/* MAC addresses are 6 bytes */
+		if (arph.ar_hln != ETH_ALEN)
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_ARP_SRC_MAC) {
+			if (skb_copy_bits(skb, sizeof(struct arphdr), &mac,
+			    ETH_ALEN))
+				return EBT_NOMATCH;
+			verdict = 0;
+			for (i = 0; i < 6; i++)
+				verdict |= (mac[i] ^ info->smaddr[i]) &
+				       info->smmsk[i];
+			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
+				return EBT_NOMATCH;
+		}
+
+		if (info->bitmask & EBT_ARP_DST_MAC) {
+			if (skb_copy_bits(skb, sizeof(struct arphdr) +
+			    arph.ar_hln + arph.ar_pln, &mac, ETH_ALEN))
+				return EBT_NOMATCH;
+			verdict = 0;
+			for (i = 0; i < 6; i++)
+				verdict |= (mac[i] ^ info->dmaddr[i]) &
+					info->dmmsk[i];
+			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
+				return EBT_NOMATCH;
+		}
+	}
+
+	return EBT_MATCH;
+}
+
+static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+	if (datalen != sizeof(struct ebt_arp_info))
+		return -EINVAL;
+	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
+	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
+	   e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_arp =
+{
+	.name		= EBT_ARP_MATCH,
+	.match		= ebt_filter_arp,
+	.check		= ebt_arp_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_arp);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_arp);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_dnat.c b/kernel/linux2.5/net/bridge/netfilter/ebt_dnat.c
new file mode 100644
index 0000000..fd9a624
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_dnat.c
@@ -0,0 +1,66 @@
+/*
+ *  ebt_dnat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
+	   ETH_ALEN * sizeof(unsigned char));
+	return info->target;
+}
+
+static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if ( (strcmp(tablename, "nat") ||
+	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+		return -EINVAL;
+	if (datalen != sizeof(struct ebt_nat_info))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target dnat =
+{
+	.name		= EBT_DNAT_TARGET,
+	.target		= ebt_target_dnat,
+	.check		= ebt_target_dnat_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&dnat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&dnat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_ip.c b/kernel/linux2.5/net/bridge/netfilter/ebt_ip.c
new file mode 100644
index 0000000..7ef0662
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_ip.c
@@ -0,0 +1,119 @@
+/*
+ *  ebt_ip
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ip.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/module.h>
+
+struct tcpudphdr {
+	uint16_t src;
+	uint16_t dst;
+};
+
+static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+	union {struct iphdr iph; struct tcpudphdr ports;} u;
+
+	if (skb_copy_bits(skb, 0, &u.iph, sizeof(u.iph)))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_IP_TOS &&
+	   FWINV(info->tos != u.iph.tos, EBT_IP_TOS))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_IP_SOURCE &&
+	   FWINV((u.iph.saddr & info->smsk) !=
+	   info->saddr, EBT_IP_SOURCE))
+		return EBT_NOMATCH;
+	if ((info->bitmask & EBT_IP_DEST) &&
+	   FWINV((u.iph.daddr & info->dmsk) !=
+	   info->daddr, EBT_IP_DEST))
+		return EBT_NOMATCH;
+	if (info->bitmask & EBT_IP_PROTO) {
+		if (FWINV(info->protocol != u.iph.protocol, EBT_IP_PROTO))
+			return EBT_NOMATCH;
+		if (!(info->bitmask & EBT_IP_DPORT) &&
+		    !(info->bitmask & EBT_IP_SPORT))
+			return EBT_MATCH;
+		if (skb_copy_bits(skb, u.iph.ihl*4, &u.ports,
+		    sizeof(u.ports)))
+			return EBT_NOMATCH;
+		if (info->bitmask & EBT_IP_DPORT) {
+			u.ports.dst = ntohs(u.ports.dst);
+			if (FWINV(u.ports.dst < info->dport[0] ||
+			          u.ports.dst > info->dport[1],
+			          EBT_IP_DPORT))
+			return EBT_NOMATCH;
+		}
+		if (info->bitmask & EBT_IP_SPORT) {
+			u.ports.src = ntohs(u.ports.src);
+			if (FWINV(u.ports.src < info->sport[0] ||
+			          u.ports.src > info->sport[1],
+			          EBT_IP_SPORT))
+			return EBT_NOMATCH;
+		}
+	}
+	return EBT_MATCH;
+}
+
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+	if (datalen != sizeof(struct ebt_ip_info))
+		return -EINVAL;
+	if (e->ethproto != __constant_htons(ETH_P_IP) ||
+	   e->invflags & EBT_IPROTO)
+		return -EINVAL;
+	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+		return -EINVAL;
+	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
+		if (info->invflags & EBT_IP_PROTO)
+			return -EINVAL;
+		if (info->protocol != IPPROTO_TCP &&
+		    info->protocol != IPPROTO_UDP)
+			 return -EINVAL;
+	}
+	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
+		return -EINVAL;
+	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_ip =
+{
+	.name		= EBT_IP_MATCH,
+	.match		= ebt_filter_ip,
+	.check		= ebt_ip_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_ip);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_ip);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_limit.c b/kernel/linux2.5/net/bridge/netfilter/ebt_limit.c
new file mode 100644
index 0000000..e504705
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_limit.c
@@ -0,0 +1,102 @@
+/*
+ *  ebt_limit
+ *
+ *	Authors:
+ *	Tom Marshall <tommy@home.tig-grr.com>
+ *
+ *	Mostly copied from netfilter's ipt_limit.c, see that file for explanation
+ *
+ *  September, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_limit.h>
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
+
+#define CREDITS_PER_JIFFY 128
+
+static int ebt_limit_match(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+	unsigned long now = jiffies;
+
+	spin_lock_bh(&limit_lock);
+	info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
+	if (info->credit > info->credit_cap)
+		info->credit = info->credit_cap;
+
+	if (info->credit >= info->cost) {
+		/* We're not limited. */
+		info->credit -= info->cost;
+		spin_unlock_bh(&limit_lock);
+		return EBT_MATCH;
+	}
+
+	spin_unlock_bh(&limit_lock);
+	return EBT_NOMATCH;
+}
+
+/* Precision saver. */
+static u_int32_t
+user2credits(u_int32_t user)
+{
+	/* If multiplying would overflow... */
+	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+		/* Divide first. */
+		return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
+
+	return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
+}
+
+static int ebt_limit_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
+
+	if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
+		return -EINVAL;
+
+	/* Check for overflow. */
+	if (info->burst == 0
+	    || user2credits(info->avg * info->burst) < user2credits(info->avg)) {
+		printk("Overflow in ebt_limit: %u/%u\n",
+			info->avg, info->burst);
+		return -EINVAL;
+	}
+
+	/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
+	info->prev = jiffies;
+	info->credit = user2credits(info->avg * info->burst);
+	info->credit_cap = user2credits(info->avg * info->burst);
+	info->cost = user2credits(info->avg);
+	return 0;
+}
+
+static struct ebt_match ebt_limit_reg =
+{
+	.name		= EBT_LIMIT_MATCH,
+	.match		= ebt_limit_match,
+	.check		= ebt_limit_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&ebt_limit_reg);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&ebt_limit_reg);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_log.c b/kernel/linux2.5/net/bridge/netfilter/ebt_log.c
new file mode 100644
index 0000000..e7a3ef4
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_log.c
@@ -0,0 +1,158 @@
+/*
+ *  ebt_log
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_log.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/spinlock.h>
+
+static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
+
+static int ebt_log_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_log_info *info = (struct ebt_log_info *)data;
+
+	if (datalen != sizeof(struct ebt_log_info))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_LOG_MASK)
+		return -EINVAL;
+	if (info->loglevel >= 8)
+		return -EINVAL;
+	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+	return 0;
+}
+
+struct tcpudphdr
+{
+	uint16_t src;
+	uint16_t dst;
+};
+
+struct arppayload
+{
+	unsigned char mac_src[ETH_ALEN];
+	unsigned char ip_src[4];
+	unsigned char mac_dst[ETH_ALEN];
+	unsigned char ip_dst[4];
+};
+
+static void print_MAC(unsigned char *p)
+{
+	int i;
+
+	for (i = 0; i < ETH_ALEN; i++, p++)
+		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+}
+
+#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
+static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out, const void *data, unsigned int datalen)
+{
+	struct ebt_log_info *info = (struct ebt_log_info *)data;
+	char level_string[4] = "< >";
+	union {struct iphdr iph; struct tcpudphdr ports;
+	       struct arphdr arph; struct arppayload arpp;} u;
+
+	level_string[1] = '0' + info->loglevel;
+	spin_lock_bh(&ebt_log_lock);
+	printk(level_string);
+	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
+	   out ? out->name : "");
+
+	printk("MAC source = ");
+	print_MAC((skb->mac.ethernet)->h_source);
+	printk("MAC dest = ");
+	print_MAC((skb->mac.ethernet)->h_dest);
+
+	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
+
+	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
+	   htons(ETH_P_IP)){
+		if (skb_copy_bits(skb, 0, &u.iph, sizeof(u.iph))) {
+			printk(" INCOMPLETE IP header");
+			goto out;
+		}
+		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+		   NIPQUAD(u.iph.saddr), NIPQUAD(u.iph.daddr));
+		printk(" IP tos=0x%02X, IP proto=%d", u.iph.tos,
+		       u.iph.protocol);
+		if (u.iph.protocol == IPPROTO_TCP ||
+		    u.iph.protocol == IPPROTO_UDP) {
+			if (skb_copy_bits(skb, u.iph.ihl*4, &u.ports,
+			    sizeof(u.ports))) {
+				printk(" INCOMPLETE TCP/UDP header");
+				goto out;
+			}
+			printk(" SPT=%u DPT=%u", ntohs(u.ports.src),
+			   ntohs(u.ports.dst));
+		}
+		goto out;
+	}
+
+	if ((info->bitmask & EBT_LOG_ARP) &&
+	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
+	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
+		if (skb_copy_bits(skb, 0, &u.arph, sizeof(u.arph))) {
+			printk(" INCOMPLETE ARP header");
+			goto out;
+		}
+		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+		       ntohs(u.arph.ar_hrd), ntohs(u.arph.ar_pro),
+		       ntohs(u.arph.ar_op));
+
+		/* If it's for Ethernet and the lengths are OK,
+		 * then log the ARP payload */
+		if (u.arph.ar_hrd == __constant_htons(1) &&
+		    u.arph.ar_hln == ETH_ALEN &&
+		    u.arph.ar_pln == sizeof(uint32_t)) {
+			if (skb_copy_bits(skb, sizeof(u.arph), &u.arpp,
+			    sizeof(u.arpp))) {
+				printk(" INCOMPLETE ARP payload");
+				goto out;
+			}
+			printk(" ARP MAC SRC=");
+			print_MAC(u.arpp.mac_src);
+			printk(" ARP IP SRC=%u.%u.%u.%u",
+			       myNIPQUAD(u.arpp.ip_src));
+			printk(" ARP MAC DST=");
+			print_MAC(u.arpp.mac_dst);
+			printk(" ARP IP DST=%u.%u.%u.%u",
+			       myNIPQUAD(u.arpp.ip_dst));
+		}
+	}
+out:
+	printk("\n");
+	spin_unlock_bh(&ebt_log_lock);
+}
+
+static struct ebt_watcher log =
+{
+	.name		= EBT_LOG_WATCHER,
+	.watcher	= ebt_log,
+	.check		= ebt_log_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_watcher(&log);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_watcher(&log);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_mark.c b/kernel/linux2.5/net/bridge/netfilter/ebt_mark.c
new file mode 100644
index 0000000..b7a87f6
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_mark.c
@@ -0,0 +1,68 @@
+/*
+ *  ebt_mark
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  July, 2002
+ *
+ */
+
+/* The mark target can be used in any chain,
+ * I believe adding a mangle table just for marking is total overkill.
+ * Marking a frame doesn't really change anything in the frame anyway.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+#include <linux/module.h>
+
+static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+	if ((*pskb)->nfmark != info->mark) {
+		(*pskb)->nfmark = info->mark;
+		(*pskb)->nfcache |= NFC_ALTERED;
+	}
+	return info->target;
+}
+
+static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+	if (datalen != sizeof(struct ebt_mark_t_info))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target mark_target =
+{
+	.name		= EBT_MARK_TARGET,
+	.target		= ebt_target_mark,
+	.check		= ebt_target_mark_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&mark_target);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&mark_target);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_mark_m.c b/kernel/linux2.5/net/bridge/netfilter/ebt_mark_m.c
new file mode 100644
index 0000000..c710b4e
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_mark_m.c
@@ -0,0 +1,62 @@
+/*
+ *  ebt_mark_m
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  July, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+#include <linux/module.h>
+
+static int ebt_filter_mark(const struct sk_buff *skb,
+   const struct net_device *in, const struct net_device *out, const void *data,
+   unsigned int datalen)
+{
+	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+	if (info->bitmask & EBT_MARK_OR)
+		return !(!!(skb->nfmark & info->mask) ^ info->invert);
+	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
+}
+
+static int ebt_mark_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+	if (datalen != sizeof(struct ebt_mark_m_info))
+		return -EINVAL;
+	if (info->bitmask & ~EBT_MARK_MASK)
+		return -EINVAL;
+	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+		return -EINVAL;
+	if (!info->bitmask)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_match filter_mark =
+{
+	.name		= EBT_MARK_MATCH,
+	.match		= ebt_filter_mark,
+	.check		= ebt_mark_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_mark);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_mark);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_pkttype.c b/kernel/linux2.5/net/bridge/netfilter/ebt_pkttype.c
new file mode 100644
index 0000000..f6458ad
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_pkttype.c
@@ -0,0 +1,59 @@
+/*
+ *  ebt_pkttype
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2003
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_pkttype.h>
+#include <linux/module.h>
+
+static int ebt_filter_pkttype(const struct sk_buff *skb,
+   const struct net_device *in,
+   const struct net_device *out,
+   const void *data,
+   unsigned int datalen)
+{
+	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+	return (skb->pkt_type != info->pkt_type) ^ info->invert;
+}
+
+static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
+
+	if (datalen != sizeof(struct ebt_pkttype_info))
+		return -EINVAL;
+	if (info->invert != 0 && info->invert != 1)
+		return -EINVAL;
+	/* Allow any pkt_type value */
+	return 0;
+}
+
+static struct ebt_match filter_pkttype =
+{
+	.name		= EBT_PKTTYPE_MATCH,
+	.match		= ebt_filter_pkttype,
+	.check		= ebt_pkttype_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_match(&filter_pkttype);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_pkttype);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_redirect.c b/kernel/linux2.5/net/bridge/netfilter/ebt_redirect.c
new file mode 100644
index 0000000..d482b3d
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_redirect.c
@@ -0,0 +1,72 @@
+/*
+ *  ebt_redirect
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_redirect.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+	if (hooknr != NF_BR_BROUTING)
+		memcpy((**pskb).mac.ethernet->h_dest,
+		   in->br_port->br->dev.dev_addr, ETH_ALEN);
+	else {
+		memcpy((**pskb).mac.ethernet->h_dest,
+		   in->dev_addr, ETH_ALEN);
+		(*pskb)->pkt_type = PACKET_HOST;
+	}
+	return info->target;
+}
+
+static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+	if (datalen != sizeof(struct ebt_redirect_info))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target redirect_target =
+{
+	.name		= EBT_REDIRECT_TARGET,
+	.target		= ebt_target_redirect,
+	.check		= ebt_target_redirect_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&redirect_target);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&redirect_target);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_snat.c b/kernel/linux2.5/net/bridge/netfilter/ebt_snat.c
new file mode 100644
index 0000000..3c0a650
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_snat.c
@@ -0,0 +1,65 @@
+/*
+ *  ebt_snat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+
+static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
+	   ETH_ALEN * sizeof(unsigned char));
+	return info->target;
+}
+
+static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+	if (datalen != sizeof(struct ebt_nat_info))
+		return -EINVAL;
+	if (BASE_CHAIN && info->target == EBT_RETURN)
+		return -EINVAL;
+	CLEAR_BASE_CHAIN_BIT;
+	if (strcmp(tablename, "nat"))
+		return -EINVAL;
+	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+		return -EINVAL;
+	if (INVALID_TARGET)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_target snat =
+{
+	.name		= EBT_SNAT_TARGET,
+	.target		= ebt_target_snat,
+	.check		= ebt_target_snat_check,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	return ebt_register_target(&snat);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_target(&snat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebt_vlan.c b/kernel/linux2.5/net/bridge/netfilter/ebt_vlan.c
new file mode 100644
index 0000000..54120a7
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebt_vlan.c
@@ -0,0 +1,193 @@
+/*
+ * Description: EBTables 802.1Q match extension kernelspace module.
+ * Authors: Nick Fedchik <nick@fedchik.org.ua>
+ *          Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_vlan.h>
+
+static unsigned char debug;
+#define MODULE_VERSION "0.6"
+
+MODULE_PARM(debug, "0-1b");
+MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
+MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
+MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
+		   MODULE_VERSION);
+MODULE_LICENSE("GPL");
+
+
+#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG "ebt_vlan: " __VA_ARGS__)
+#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return EBT_NOMATCH;}
+
+static int
+ebt_filter_vlan(const struct sk_buff *skb,
+		const struct net_device *in,
+		const struct net_device *out,
+		const void *data, unsigned int datalen)
+{
+	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+	struct vlan_ethhdr frame;
+
+	unsigned short TCI;	/* Whole TCI, given from parsed frame */
+	unsigned short id;	/* VLAN ID, given from frame TCI */
+	unsigned char prio;	/* user_priority, given from frame TCI */
+	/* VLAN encapsulated Type/Length field, given from orig frame */
+	unsigned short encap;
+
+	if (skb_copy_bits(skb, 0, &frame, sizeof(frame)))
+		return EBT_NOMATCH;
+
+	/* Tag Control Information (TCI) consists of the following elements:
+	 * - User_priority. The user_priority field is three bits in length,
+	 * interpreted as a binary number.
+	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator
+	 * (CFI) is a single bit flag value. Currently ignored.
+	 * - VLAN Identifier (VID). The VID is encoded as
+	 * an unsigned binary number. */
+	TCI = ntohs(frame.h_vlan_TCI);
+	id = TCI & VLAN_VID_MASK;
+	prio = (TCI >> 13) & 0x7;
+	encap = frame.h_vlan_encapsulated_proto;
+
+	/* Checking VLAN Identifier (VID) */
+	if (GET_BITMASK(EBT_VLAN_ID))
+		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
+
+	/* Checking user_priority */
+	if (GET_BITMASK(EBT_VLAN_PRIO))
+		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
+
+	/* Checking Encapsulated Proto (Length/Type) field */
+	if (GET_BITMASK(EBT_VLAN_ENCAP))
+		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
+
+	return EBT_MATCH;
+}
+
+static int
+ebt_check_vlan(const char *tablename,
+	       unsigned int hooknr,
+	       const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+
+	/* Parameters buffer overflow check */
+	if (datalen != sizeof(struct ebt_vlan_info)) {
+		DEBUG_MSG
+		    ("passed size %d is not eq to ebt_vlan_info (%Zd)\n",
+		     datalen, sizeof(struct ebt_vlan_info));
+		return -EINVAL;
+	}
+
+	/* Is it 802.1Q frame checked? */
+	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
+		DEBUG_MSG
+		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+		     (unsigned short) ntohs(e->ethproto));
+		return -EINVAL;
+	}
+
+	/* Check for bitmask range
+	 * True if even one bit is out of mask */
+	if (info->bitmask & ~EBT_VLAN_MASK) {
+		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
+			  info->bitmask, EBT_VLAN_MASK);
+		return -EINVAL;
+	}
+
+	/* Check for inversion flags range */
+	if (info->invflags & ~EBT_VLAN_MASK) {
+		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
+			  info->invflags, EBT_VLAN_MASK);
+		return -EINVAL;
+	}
+
+	/* Reserved VLAN ID (VID) values
+	 * -----------------------------
+	 * 0 - The null VLAN ID. 
+	 * 1 - The default Port VID (PVID)
+	 * 0x0FFF - Reserved for implementation use. 
+	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096. */
+	if (GET_BITMASK(EBT_VLAN_ID)) {
+		if (!!info->id) { /* if id!=0 => check vid range */
+			if (info->id > VLAN_GROUP_ARRAY_LEN) {
+				DEBUG_MSG
+				    ("id %d is out of range (1-4096)\n",
+				     info->id);
+				return -EINVAL;
+			}
+			/* Note: This is valid VLAN-tagged frame point.
+			 * Any value of user_priority are acceptable, 
+			 * but should be ignored according to 802.1Q Std.
+			 * So we just drop the prio flag. */
+			info->bitmask &= ~EBT_VLAN_PRIO;
+		}
+		/* Else, id=0 (null VLAN ID)  => user_priority range (any?) */
+	}
+
+	if (GET_BITMASK(EBT_VLAN_PRIO)) {
+		if ((unsigned char) info->prio > 7) {
+			DEBUG_MSG("prio %d is out of range (0-7)\n",
+			     info->prio);
+			return -EINVAL;
+		}
+	}
+	/* Check for encapsulated proto range - it is possible to be
+	 * any value for u_short range.
+	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS */
+	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
+		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
+			DEBUG_MSG
+			    ("encap frame length %d is less than minimal\n",
+			     ntohs(info->encap));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static struct ebt_match filter_vlan = {
+	.name		= EBT_VLAN_MATCH,
+	.match		= ebt_filter_vlan,
+	.check		= ebt_check_vlan,
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	DEBUG_MSG("ebtables 802.1Q extension module v"
+		  MODULE_VERSION "\n");
+	DEBUG_MSG("module debug=%d\n", !!debug);
+	return ebt_register_match(&filter_vlan);
+}
+
+static void __exit fini(void)
+{
+	ebt_unregister_match(&filter_vlan);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebtable_broute.c b/kernel/linux2.5/net/bridge/netfilter/ebtable_broute.c
new file mode 100644
index 0000000..1767c94
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebtable_broute.c
@@ -0,0 +1,86 @@
+/*
+ *  ebtable_broute
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  This table lets you choose between routing and bridging for frames
+ *  entering on a bridge enslaved nic. This table is traversed before any
+ *  other ebtables table. See net/bridge/br_input.c.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#include <linux/if_bridge.h>
+
+/* EBT_ACCEPT means the frame will be bridged
+ * EBT_DROP means the frame will be routed
+ */
+static struct ebt_entries initial_chain = {
+	.name		= "BROUTING",
+	.policy		= EBT_ACCEPT,
+};
+
+static struct ebt_replace initial_table =
+{
+	.name		= "broute",
+	.valid_hooks	= 1 << NF_BR_BROUTING,
+	.entries_size	= sizeof(struct ebt_entries),
+	.hook_entry	= {
+		[NF_BR_BROUTING]	= &initial_chain,
+	},
+	.entries	= (char *)&initial_chain,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~(1 << NF_BR_BROUTING))
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table broute_table =
+{
+	.name		= "broute",
+	.table		= &initial_table,
+	.valid_hooks	= 1 << NF_BR_BROUTING,
+	.lock		= RW_LOCK_UNLOCKED,
+	.check		= check,
+	.me		= THIS_MODULE,
+};
+
+static int ebt_broute(struct sk_buff **pskb)
+{
+	int ret;
+
+	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
+	   &broute_table);
+	if (ret == NF_DROP)
+		return 1; /* route it */
+	return 0; /* bridge it */
+}
+
+static int __init init(void)
+{
+	int ret;
+
+	ret = ebt_register_table(&broute_table);
+	if (ret < 0)
+		return ret;
+	/* see br_input.c */
+	br_should_route_hook = ebt_broute;
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	br_should_route_hook = NULL;
+	synchronize_net();
+	ebt_unregister_table(&broute_table);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebtable_filter.c b/kernel/linux2.5/net/bridge/netfilter/ebtable_filter.c
new file mode 100644
index 0000000..c18666e
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebtable_filter.c
@@ -0,0 +1,123 @@
+/*
+ *  ebtable_filter
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+   (1 << NF_BR_LOCAL_OUT))
+
+static struct ebt_entries initial_chains[] =
+{
+	{
+		.name	= "INPUT",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "FORWARD",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "OUTPUT",
+		.policy	= EBT_ACCEPT,
+	},
+};
+
+static struct ebt_replace initial_table =
+{
+	.name		= "filter",
+	.valid_hooks	= FILTER_VALID_HOOKS,
+	.entries_size	= 3 * sizeof(struct ebt_entries),
+	.hook_entry	= {
+		[NF_BR_LOCAL_IN]	= &initial_chains[0],
+		[NF_BR_FORWARD]		= &initial_chains[1],
+		[NF_BR_LOCAL_OUT]	= &initial_chains[2],
+	},
+	.entries	= (char *)initial_chains,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~FILTER_VALID_HOOKS)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table frame_filter =
+{ 
+	.name		= "filter",
+	.table		= &initial_table,
+	.valid_hooks	= FILTER_VALID_HOOKS, 
+	.lock		= RW_LOCK_UNLOCKED,
+	.check		= check,
+	.me		= THIS_MODULE,
+};
+
+static unsigned int
+ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
+   const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_filter);
+}
+
+static struct nf_hook_ops ebt_ops_filter[] = {
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_LOCAL_IN,
+		.priority	= NF_BR_PRI_FILTER_BRIDGED,
+	},
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_FORWARD,
+		.priority	= NF_BR_PRI_FILTER_BRIDGED,
+	},
+	{
+		.hook		= ebt_hook,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_LOCAL_OUT,
+		.priority	= NF_BR_PRI_FILTER_OTHER,
+	},
+};
+
+static int __init init(void)
+{
+	int i, j, ret;
+
+	ret = ebt_register_table(&frame_filter);
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
+		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
+			goto cleanup;
+	return ret;
+cleanup:
+	for (j = 0; j < i; j++)
+		nf_unregister_hook(&ebt_ops_filter[j]);
+	ebt_unregister_table(&frame_filter);
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
+		nf_unregister_hook(&ebt_ops_filter[i]);
+	ebt_unregister_table(&frame_filter);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebtable_nat.c b/kernel/linux2.5/net/bridge/netfilter/ebtable_nat.c
new file mode 100644
index 0000000..828cac2
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebtable_nat.c
@@ -0,0 +1,130 @@
+/*
+ *  ebtable_nat
+ *
+ *	Authors:
+ *	Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+   (1 << NF_BR_POST_ROUTING))
+
+static struct ebt_entries initial_chains[] =
+{
+	{
+		.name	= "PREROUTING",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "OUTPUT",
+		.policy	= EBT_ACCEPT,
+	},
+	{
+		.name	= "POSTROUTING",
+		.policy	= EBT_ACCEPT,
+	}
+};
+
+static struct ebt_replace initial_table =
+{
+	.name		= "nat",
+	.valid_hooks	= NAT_VALID_HOOKS,
+	.entries_size	= 3 * sizeof(struct ebt_entries),
+	.hook_entry	= {
+		[NF_BR_PRE_ROUTING]	= &initial_chains[0],
+		[NF_BR_LOCAL_OUT]	= &initial_chains[1],
+		[NF_BR_POST_ROUTING]	= &initial_chains[2],
+	},
+	.entries	= (char *)initial_chains,
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+	if (valid_hooks & ~NAT_VALID_HOOKS)
+		return -EINVAL;
+	return 0;
+}
+
+static struct ebt_table frame_nat =
+{
+	.name		= "nat",
+	.table		= &initial_table,
+	.valid_hooks	= NAT_VALID_HOOKS,
+	.lock		= RW_LOCK_UNLOCKED,
+	.check		= check,
+	.me		= THIS_MODULE,
+};
+
+static unsigned int
+ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static unsigned int
+ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
+   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static struct nf_hook_ops ebt_ops_nat[] = {
+	{
+		.hook		= ebt_nat_dst,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_LOCAL_OUT,
+		.priority	= NF_BR_PRI_NAT_DST_OTHER,
+	},
+	{
+		.hook		= ebt_nat_src,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_POST_ROUTING,
+		.priority	= NF_BR_PRI_NAT_SRC,
+	},
+	{
+		.hook		= ebt_nat_dst,
+		.owner		= THIS_MODULE,
+		.pf		= PF_BRIDGE,
+		.hooknum	= NF_BR_PRE_ROUTING,
+		.priority	= NF_BR_PRI_NAT_DST_BRIDGED,
+	},
+};
+
+static int __init init(void)
+{
+	int i, ret, j;
+
+	ret = ebt_register_table(&frame_nat);
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
+		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
+			goto cleanup;
+	return ret;
+cleanup:
+	for (j = 0; j < i; j++)
+		nf_unregister_hook(&ebt_ops_nat[j]);
+	ebt_unregister_table(&frame_nat);
+	return ret;
+}
+
+static void __exit fini(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
+		nf_unregister_hook(&ebt_ops_nat[i]);
+	ebt_unregister_table(&frame_nat);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/bridge/netfilter/ebtables.c b/kernel/linux2.5/net/bridge/netfilter/ebtables.c
new file mode 100644
index 0000000..7f32804
--- /dev/null
+++ b/kernel/linux2.5/net/bridge/netfilter/ebtables.c
@@ -0,0 +1,1500 @@
+/*
+ *  ebtables
+ *
+ *  Author:
+ *  Bart De Schuymer		<bdschuym@pandora.be>
+ *
+ *  ebtables.c,v 2.0, July, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ */
+
+/* used for print_string */
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <net/sock.h>
+/* needed for logical [in,out]-dev filtering */
+#include "../br_private.h"
+
+/* list_named_find */
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+/* use this for remote debugging
+ * Copyright (C) 1998 by Ori Pomerantz
+ * Print the string to the appropriate tty, the one
+ * the current task uses
+ */
+static void print_string(char *str)
+{
+	struct tty_struct *my_tty;
+
+	/* The tty for the current task */
+	my_tty = current->tty;
+	if (my_tty != NULL) {
+		my_tty->driver->write(my_tty, 0, str, strlen(str));
+		my_tty->driver->write(my_tty, 0, "\015\012", 2);
+	}
+}
+
+#define BUGPRINT(args) print_string(args);
+#else
+#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+                                         "report to author: "format, ## args)
+/* #define BUGPRINT(format, args...) */
+#endif
+#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
+                                         ": out of memory: "format, ## args)
+/* #define MEMPRINT(format, args...) */
+
+
+
+/*
+ * Each cpu has its own set of counters, so there is no need for write_lock in
+ * the softirq
+ * For reading or updating the counters, the user context needs to
+ * get a write_lock
+ */
+
+/* The size of each set of counters is altered to get cache alignment */
+#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
+#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
+#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
+   COUNTER_OFFSET(n) * cpu))
+
+
+
+static DECLARE_MUTEX(ebt_mutex);
+static LIST_HEAD(ebt_tables);
+static LIST_HEAD(ebt_targets);
+static LIST_HEAD(ebt_matches);
+static LIST_HEAD(ebt_watchers);
+
+static struct ebt_target ebt_standard_target =
+{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+
+static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+   const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out)
+{
+	w->u.watcher->watcher(skb, in, out, w->data,
+	   w->watcher_size);
+	/* watchers don't give a verdict */
+	return 0;
+}
+
+static inline int ebt_do_match (struct ebt_entry_match *m,
+   const struct sk_buff *skb, const struct net_device *in,
+   const struct net_device *out)
+{
+	return m->u.match->match(skb, in, out, m->data,
+	   m->match_size);
+}
+
+static inline int ebt_dev_check(char *entry, const struct net_device *device)
+{
+	if (*entry == '\0')
+		return 0;
+	if (!device)
+		return 1;
+	return !!strcmp(entry, device->name);
+}
+
+#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+/* process standard matches */
+static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+   const struct net_device *in, const struct net_device *out)
+{
+	int verdict, i;
+
+	if (e->bitmask & EBT_802_3) {
+		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+			return 1;
+	} else if (!(e->bitmask & EBT_NOPROTO) &&
+	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+		return 1;
+
+	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+		return 1;
+	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+		return 1;
+	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
+	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+		return 1;
+	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
+	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+		return 1;
+
+	if (e->bitmask & EBT_SOURCEMAC) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
+			   e->sourcemsk[i];
+		if (FWINV2(verdict != 0, EBT_ISOURCE) )
+			return 1;
+	}
+	if (e->bitmask & EBT_DESTMAC) {
+		verdict = 0;
+		for (i = 0; i < 6; i++)
+			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
+			   e->destmsk[i];
+		if (FWINV2(verdict != 0, EBT_IDEST) )
+			return 1;
+	}
+	return 0;
+}
+
+/* Do some firewalling */
+unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+   const struct net_device *in, const struct net_device *out,
+   struct ebt_table *table)
+{
+	int i, nentries;
+	struct ebt_entry *point;
+	struct ebt_counter *counter_base, *cb_base;
+	struct ebt_entry_target *t;
+	int verdict, sp = 0;
+	struct ebt_chainstack *cs;
+	struct ebt_entries *chaininfo;
+	char *base;
+	struct ebt_table_info *private = table->private;
+
+	read_lock_bh(&table->lock);
+	cb_base = COUNTER_BASE(private->counters, private->nentries,
+	   smp_processor_id());
+	if (private->chainstack)
+		cs = private->chainstack[smp_processor_id()];
+	else
+		cs = NULL;
+	chaininfo = private->hook_entry[hook];
+	nentries = private->hook_entry[hook]->nentries;
+	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+	/* base for chain jumps */
+	base = private->entries;
+	i = 0;
+	while (i < nentries) {
+		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
+			goto letscontinue;
+
+		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+			goto letscontinue;
+
+		/* increase counter */
+		(*(counter_base + i)).pcnt++;
+		(*(counter_base + i)).bcnt+=(**pskb).len;
+
+		/* these should only watch: not modify, nor tell us
+		   what to do with the packet */
+		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+		   out);
+
+		t = (struct ebt_entry_target *)
+		   (((char *)point) + point->target_offset);
+		/* standard target */
+		if (!t->u.target->target)
+			verdict = ((struct ebt_standard_target *)t)->verdict;
+		else
+			verdict = t->u.target->target(pskb, hook,
+			   in, out, t->data, t->target_size);
+		if (verdict == EBT_ACCEPT) {
+			read_unlock_bh(&table->lock);
+			return NF_ACCEPT;
+		}
+		if (verdict == EBT_DROP) {
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+		if (verdict == EBT_RETURN) {
+letsreturn:
+#ifdef CONFIG_NETFILTER_DEBUG
+			if (sp == 0) {
+				BUGPRINT("RETURN on base chain");
+				/* act like this is EBT_CONTINUE */
+				goto letscontinue;
+			}
+#endif
+			sp--;
+			/* put all the local variables right */
+			i = cs[sp].n;
+			chaininfo = cs[sp].chaininfo;
+			nentries = chaininfo->nentries;
+			point = cs[sp].e;
+			counter_base = cb_base +
+			   chaininfo->counter_offset;
+			continue;
+		}
+		if (verdict == EBT_CONTINUE)
+			goto letscontinue;
+#ifdef CONFIG_NETFILTER_DEBUG
+		if (verdict < 0) {
+			BUGPRINT("bogus standard verdict\n");
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+#endif
+		/* jump to a udc */
+		cs[sp].n = i + 1;
+		cs[sp].chaininfo = chaininfo;
+		cs[sp].e = (struct ebt_entry *)
+		   (((char *)point) + point->next_offset);
+		i = 0;
+		chaininfo = (struct ebt_entries *) (base + verdict);
+#ifdef CONFIG_NETFILTER_DEBUG
+		if (chaininfo->distinguisher) {
+			BUGPRINT("jump to non-chain\n");
+			read_unlock_bh(&table->lock);
+			return NF_DROP;
+		}
+#endif
+		nentries = chaininfo->nentries;
+		point = (struct ebt_entry *)chaininfo->data;
+		counter_base = cb_base + chaininfo->counter_offset;
+		sp++;
+		continue;
+letscontinue:
+		point = (struct ebt_entry *)
+		   (((char *)point) + point->next_offset);
+		i++;
+	}
+
+	/* I actually like this :) */
+	if (chaininfo->policy == EBT_RETURN)
+		goto letsreturn;
+	if (chaininfo->policy == EBT_ACCEPT) {
+		read_unlock_bh(&table->lock);
+		return NF_ACCEPT;
+	}
+	read_unlock_bh(&table->lock);
+	return NF_DROP;
+}
+
+/* If it succeeds, returns element and locks mutex */
+static inline void *
+find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
+   struct semaphore *mutex)
+{
+	void *ret;
+
+	*error = down_interruptible(mutex);
+	if (*error != 0)
+		return NULL;
+
+	ret = list_named_find(head, name);
+	if (!ret) {
+		*error = -ENOENT;
+		up(mutex);
+	}
+	return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
+   int *error, struct semaphore *mutex)
+{
+	void *ret;
+
+	ret = find_inlist_lock_noload(head, name, error, mutex);
+	if (!ret) {
+		request_module("%s%s", prefix, name);
+		ret = find_inlist_lock_noload(head, name, error, mutex);
+	}
+	return ret;
+}
+#endif
+
+static inline struct ebt_table *
+find_table_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+}
+
+static inline struct ebt_match *
+find_match_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_watcher *
+find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_target *
+find_target_lock(const char *name, int *error, struct semaphore *mutex)
+{
+	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
+}
+
+static inline int
+ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+   const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+	struct ebt_match *match;
+	int ret;
+
+	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+	   ((char *)e) + e->watchers_offset)
+		return -EINVAL;
+	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+	if (!match)
+		return ret;
+	m->u.match = match;
+	if (!try_module_get(match->me)) {
+		up(&ebt_mutex);
+		return -ENOENT;
+	}
+	up(&ebt_mutex);
+	if (match->check &&
+	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+		BUGPRINT("match->check failed\n");
+		module_put(match->me);
+		return -EINVAL;
+	}
+	(*cnt)++;
+	return 0;
+}
+
+static inline int
+ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+   const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+	struct ebt_watcher *watcher;
+	int ret;
+
+	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+	   ((char *)e) + e->target_offset)
+		return -EINVAL;
+	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+	if (!watcher)
+		return ret;
+	w->u.watcher = watcher;
+	if (!try_module_get(watcher->me)) {
+		up(&ebt_mutex);
+		return -ENOENT;
+	}
+	up(&ebt_mutex);
+	if (watcher->check &&
+	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+		BUGPRINT("watcher->check failed\n");
+		module_put(watcher->me);
+		return -EINVAL;
+	}
+	(*cnt)++;
+	return 0;
+}
+
+/*
+ * this one is very careful, as it is the first function
+ * to parse the userspace data
+ */
+static inline int
+ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+   struct ebt_table_info *newinfo, char *base, char *limit,
+   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+{
+	int i;
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if ( (char *)hook_entries[i] - base ==
+		   (char *)e - newinfo->entries)
+			break;
+	}
+	/* beginning of a new chain
+	   if i == NF_BR_NUMHOOKS it must be a user defined chain */
+	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+			/* we make userspace set this right,
+			   so there is no misunderstanding */
+			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+			         "in distinguisher\n");
+			return -EINVAL;
+		}
+		/* this checks if the previous chain has as many entries
+		   as it said it has */
+		if (*n != *cnt) {
+			BUGPRINT("nentries does not equal the nr of entries "
+		                 "in the chain\n");
+			return -EINVAL;
+		}
+		/* before we look at the struct, be sure it is not too big */
+		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
+		   > limit) {
+			BUGPRINT("entries_size too small\n");
+			return -EINVAL;
+		}
+		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+			/* only RETURN from udc */
+			if (i != NF_BR_NUMHOOKS ||
+			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
+				BUGPRINT("bad policy\n");
+				return -EINVAL;
+			}
+		}
+		if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */
+			(*udc_cnt)++;
+		else
+			newinfo->hook_entry[i] = (struct ebt_entries *)e;
+		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
+			BUGPRINT("counter_offset != totalcnt");
+			return -EINVAL;
+		}
+		*n = ((struct ebt_entries *)e)->nentries;
+		*cnt = 0;
+		return 0;
+	}
+	/* a plain old entry, heh */
+	if (sizeof(struct ebt_entry) > e->watchers_offset ||
+	   e->watchers_offset > e->target_offset ||
+	   e->target_offset >= e->next_offset) {
+		BUGPRINT("entry offsets not in right order\n");
+		return -EINVAL;
+	}
+	/* this is not checked anywhere else */
+	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
+		BUGPRINT("target size too small\n");
+		return -EINVAL;
+	}
+
+	(*cnt)++;
+	(*totalcnt)++;
+	return 0;
+}
+
+struct ebt_cl_stack
+{
+	struct ebt_chainstack cs;
+	int from;
+	unsigned int hookmask;
+};
+
+/*
+ * we need these positions to check that the jumps to a different part of the
+ * entries is a jump to the beginning of a new chain.
+ */
+static inline int
+ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
+   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
+   struct ebt_cl_stack *udc)
+{
+	int i;
+
+	/* we're only interested in chain starts */
+	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+		return 0;
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
+			break;
+	}
+	/* only care about udc */
+	if (i != NF_BR_NUMHOOKS)
+		return 0;
+
+	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
+	/* these initialisations are depended on later in check_chainloops() */
+	udc[*n].cs.n = 0;
+	udc[*n].hookmask = 0;
+
+	(*n)++;
+	return 0;
+}
+
+static inline int
+ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+	if (m->u.match->destroy)
+		m->u.match->destroy(m->data, m->match_size);
+	module_put(m->u.match->me);
+
+	return 0;
+}
+
+static inline int
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+{
+	if (i && (*i)-- == 0)
+		return 1;
+	if (w->u.watcher->destroy)
+		w->u.watcher->destroy(w->data, w->watcher_size);
+	module_put(w->u.watcher->me);
+
+	return 0;
+}
+
+static inline int
+ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+{
+	struct ebt_entry_target *t;
+
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+	/* we're done */
+	if (cnt && (*cnt)-- == 0)
+		return 1;
+	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	if (t->u.target->destroy)
+		t->u.target->destroy(t->data, t->target_size);
+	module_put(t->u.target->me);
+
+	return 0;
+}
+
+static inline int
+ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+   const char *name, unsigned int *cnt, unsigned int valid_hooks,
+   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+{
+	struct ebt_entry_target *t;
+	struct ebt_target *target;
+	unsigned int i, j, hook = 0, hookmask = 0;
+	int ret;
+
+	/* don't mess with the struct ebt_entries */
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+
+	if (e->bitmask & ~EBT_F_MASK) {
+		BUGPRINT("Unknown flag for bitmask\n");
+		return -EINVAL;
+	}
+	if (e->invflags & ~EBT_INV_MASK) {
+		BUGPRINT("Unknown flag for inv bitmask\n");
+		return -EINVAL;
+	}
+	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+		BUGPRINT("NOPROTO & 802_3 not allowed\n");
+		return -EINVAL;
+	}
+	/* what hook do we belong to? */
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if ((valid_hooks & (1 << i)) == 0)
+			continue;
+		if ((char *)newinfo->hook_entry[i] < (char *)e)
+			hook = i;
+		else
+			break;
+	}
+	/* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
+	   a base chain */
+	if (i < NF_BR_NUMHOOKS)
+		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+	else {
+		for (i = 0; i < udc_cnt; i++)
+			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+				break;
+		if (i == 0)
+			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+		else
+			hookmask = cl_s[i - 1].hookmask;
+	}
+	i = 0;
+	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+	if (ret != 0)
+		goto cleanup_matches;
+	j = 0;
+	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+	if (ret != 0)
+		goto cleanup_watchers;
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+	if (!target)
+		goto cleanup_watchers;
+	if (!try_module_get(target->me)) {
+		up(&ebt_mutex);
+		ret = -ENOENT;
+		goto cleanup_watchers;
+	}
+	up(&ebt_mutex);
+
+	t->u.target = target;
+	if (t->u.target == &ebt_standard_target) {
+		if (e->target_offset + sizeof(struct ebt_standard_target) >
+		   e->next_offset) {
+			BUGPRINT("Standard target size too big\n");
+			ret = -EFAULT;
+			goto cleanup_watchers;
+		}
+		if (((struct ebt_standard_target *)t)->verdict <
+		   -NUM_STANDARD_TARGETS) {
+			BUGPRINT("Invalid standard target\n");
+			ret = -EFAULT;
+			goto cleanup_watchers;
+		}
+	} else if ((e->target_offset + t->target_size +
+	   sizeof(struct ebt_entry_target) > e->next_offset) ||
+	   (t->u.target->check &&
+	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+		module_put(t->u.target->me);
+		ret = -EFAULT;
+		goto cleanup_watchers;
+	}
+	(*cnt)++;
+	return 0;
+cleanup_watchers:
+	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+cleanup_matches:
+	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+	return ret;
+}
+
+/*
+ * checks for loops and sets the hook mask for udc
+ * the hook mask for udc tells us from which base chains the udc can be
+ * accessed. This mask is a parameter to the check() functions of the extensions
+ */
+static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
+   unsigned int udc_cnt, unsigned int hooknr, char *base)
+{
+	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
+	struct ebt_entry *e = (struct ebt_entry *)chain->data;
+	struct ebt_entry_target *t;
+
+	while (pos < nentries || chain_nr != -1) {
+		/* end of udc, go back one 'recursion' step */
+		if (pos == nentries) {
+			/* put back values of the time when this chain was called */
+			e = cl_s[chain_nr].cs.e;
+			if (cl_s[chain_nr].from != -1)
+				nentries =
+				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+			else
+				nentries = chain->nentries;
+			pos = cl_s[chain_nr].cs.n;
+			/* make sure we won't see a loop that isn't one */
+			cl_s[chain_nr].cs.n = 0;
+			chain_nr = cl_s[chain_nr].from;
+			if (pos == nentries)
+				continue;
+		}
+		t = (struct ebt_entry_target *)
+		   (((char *)e) + e->target_offset);
+		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+			goto letscontinue;
+		if (e->target_offset + sizeof(struct ebt_standard_target) >
+		   e->next_offset) {
+			BUGPRINT("Standard target size too big\n");
+			return -1;
+		}
+		verdict = ((struct ebt_standard_target *)t)->verdict;
+		if (verdict >= 0) { /* jump to another chain */
+			struct ebt_entries *hlp2 =
+			   (struct ebt_entries *)(base + verdict);
+			for (i = 0; i < udc_cnt; i++)
+				if (hlp2 == cl_s[i].cs.chaininfo)
+					break;
+			/* bad destination or loop */
+			if (i == udc_cnt) {
+				BUGPRINT("bad destination\n");
+				return -1;
+			}
+			if (cl_s[i].cs.n) {
+				BUGPRINT("loop\n");
+				return -1;
+			}
+			/* this can't be 0, so the above test is correct */
+			cl_s[i].cs.n = pos + 1;
+			pos = 0;
+			cl_s[i].cs.e = ((void *)e + e->next_offset);
+			e = (struct ebt_entry *)(hlp2->data);
+			nentries = hlp2->nentries;
+			cl_s[i].from = chain_nr;
+			chain_nr = i;
+			/* this udc is accessible from the base chain for hooknr */
+			cl_s[i].hookmask |= (1 << hooknr);
+			continue;
+		}
+letscontinue:
+		e = (void *)e + e->next_offset;
+		pos++;
+	}
+	return 0;
+}
+
+/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
+static int translate_table(struct ebt_replace *repl,
+   struct ebt_table_info *newinfo)
+{
+	unsigned int i, j, k, udc_cnt;
+	int ret;
+	struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */
+
+	i = 0;
+	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+		i++;
+	if (i == NF_BR_NUMHOOKS) {
+		BUGPRINT("No valid hooks specified\n");
+		return -EINVAL;
+	}
+	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+		BUGPRINT("Chains don't start at beginning\n");
+		return -EINVAL;
+	}
+	/* make sure chains are ordered after each other in same order
+	   as their corresponding hooks */
+	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
+		if (!(repl->valid_hooks & (1 << j)))
+			continue;
+		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+			BUGPRINT("Hook order must be followed\n");
+			return -EINVAL;
+		}
+		i = j;
+	}
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		newinfo->hook_entry[i] = NULL;
+
+	newinfo->entries_size = repl->entries_size;
+	newinfo->nentries = repl->nentries;
+
+	/* do some early checkings and initialize some things */
+	i = 0; /* holds the expected nr. of entries for the chain */
+	j = 0; /* holds the up to now counted entries for the chain */
+	k = 0; /* holds the total nr. of entries, should equal
+	          newinfo->nentries afterwards */
+	udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
+	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+	   &udc_cnt, repl->valid_hooks);
+
+	if (ret != 0)
+		return ret;
+
+	if (i != j) {
+		BUGPRINT("nentries does not equal the nr of entries in the "
+		         "(last) chain\n");
+		return -EINVAL;
+	}
+	if (k != newinfo->nentries) {
+		BUGPRINT("Total nentries is wrong\n");
+		return -EINVAL;
+	}
+
+	/* check if all valid hooks have a chain */
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if (newinfo->hook_entry[i] == NULL &&
+		   (repl->valid_hooks & (1 << i))) {
+			BUGPRINT("Valid hook without chain\n");
+			return -EINVAL;
+		}
+	}
+
+	/* get the location of the udc, put them in an array
+	   while we're at it, allocate the chainstack */
+	if (udc_cnt) {
+		/* this will get free'd in do_replace()/ebt_register_table()
+		   if an error occurs */
+		newinfo->chainstack = (struct ebt_chainstack **)
+		   vmalloc(NR_CPUS * sizeof(struct ebt_chainstack));
+		if (!newinfo->chainstack)
+			return -ENOMEM;
+		for (i = 0; i < NR_CPUS; i++) {
+			newinfo->chainstack[i] =
+			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
+			if (!newinfo->chainstack[i]) {
+				while (i)
+					vfree(newinfo->chainstack[--i]);
+				vfree(newinfo->chainstack);
+				newinfo->chainstack = NULL;
+				return -ENOMEM;
+			}
+		}
+
+		cl_s = (struct ebt_cl_stack *)
+		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+		if (!cl_s)
+			return -ENOMEM;
+		i = 0; /* the i'th udc */
+		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
+		   repl->valid_hooks, cl_s);
+		/* sanity check */
+		if (i != udc_cnt) {
+			BUGPRINT("i != udc_cnt\n");
+			vfree(cl_s);
+			return -EFAULT;
+		}
+	}
+
+	/* Check for loops */
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (repl->valid_hooks & (1 << i))
+			if (check_chainloops(newinfo->hook_entry[i],
+			   cl_s, udc_cnt, i, newinfo->entries)) {
+				if (cl_s)
+					vfree(cl_s);
+				return -EINVAL;
+			}
+
+	/* we now know the following (along with E=mc²):
+	   - the nr of entries in each chain is right
+	   - the size of the allocated space is right
+	   - all valid hooks have a corresponding chain
+	   - there are no loops
+	   - wrong data can still be on the level of a single entry
+	   - could be there are jumps to places that are not the
+	     beginning of a chain. This can only occur in chains that
+	     are not accessible from any base chains, so we don't care. */
+
+	/* used to know what we need to clean up if something goes wrong */
+	i = 0;
+	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
+	   cl_s, udc_cnt);
+	if (ret != 0) {
+		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+		   ebt_cleanup_entry, &i);
+	}
+	if (cl_s)
+		vfree(cl_s);
+	return ret;
+}
+
+/* called under write_lock */
+static void get_counters(struct ebt_counter *oldcounters,
+   struct ebt_counter *counters, unsigned int nentries)
+{
+	int i, cpu;
+	struct ebt_counter *counter_base;
+
+	/* counters of cpu 0 */
+	memcpy(counters, oldcounters,
+	   sizeof(struct ebt_counter) * nentries);
+	/* add other counters to those of cpu 0 */
+	for (cpu = 1; cpu < NR_CPUS; cpu++) {
+		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+		for (i = 0; i < nentries; i++) {
+			counters[i].pcnt += counter_base[i].pcnt;
+			counters[i].bcnt += counter_base[i].bcnt;
+		}
+	}
+}
+
+/* replace the table */
+static int do_replace(void *user, unsigned int len)
+{
+	int ret, i, countersize;
+	struct ebt_table_info *newinfo;
+	struct ebt_replace tmp;
+	struct ebt_table *t;
+	struct ebt_counter *counterstmp = NULL;
+	/* used to be able to unlock earlier */
+	struct ebt_table_info *table;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+		return -EFAULT;
+
+	if (len != sizeof(tmp) + tmp.entries_size) {
+		BUGPRINT("Wrong len argument\n");
+		return -EINVAL;
+	}
+
+	if (tmp.entries_size == 0) {
+		BUGPRINT("Entries_size never zero\n");
+		return -EINVAL;
+	}
+	countersize = COUNTER_OFFSET(tmp.nentries) * NR_CPUS;
+	newinfo = (struct ebt_table_info *)
+	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+	if (!newinfo)
+		return -ENOMEM;
+
+	if (countersize)
+		memset(newinfo->counters, 0, countersize);
+
+	newinfo->entries = (char *)vmalloc(tmp.entries_size);
+	if (!newinfo->entries) {
+		ret = -ENOMEM;
+		goto free_newinfo;
+	}
+	if (copy_from_user(
+	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+		BUGPRINT("Couldn't copy entries from userspace\n");
+		ret = -EFAULT;
+		goto free_entries;
+	}
+
+	/* the user wants counters back
+	   the check on the size is done later, when we have the lock */
+	if (tmp.num_counters) {
+		counterstmp = (struct ebt_counter *)
+		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
+		if (!counterstmp) {
+			ret = -ENOMEM;
+			goto free_entries;
+		}
+	}
+	else
+		counterstmp = NULL;
+
+	/* this can get initialized by translate_table() */
+	newinfo->chainstack = NULL;
+	ret = translate_table(&tmp, newinfo);
+
+	if (ret != 0)
+		goto free_counterstmp;
+
+	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+	if (!t) {
+		ret = -ENOENT;
+		goto free_iterate;
+	}
+
+	/* the table doesn't like it */
+	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+		goto free_unlock;
+
+	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+		BUGPRINT("Wrong nr. of counters requested\n");
+		ret = -EINVAL;
+		goto free_unlock;
+	}
+
+	/* we have the mutex lock, so no danger in reading this pointer */
+	table = t->private;
+	/* make sure the table can only be rmmod'ed if it contains no rules */
+	if (!table->nentries && newinfo->nentries && !try_module_get(t->me)) {
+		ret = -ENOENT;
+		goto free_unlock;
+	} else if (table->nentries && !newinfo->nentries)
+		module_put(t->me);
+	/* we need an atomic snapshot of the counters */
+	write_lock_bh(&t->lock);
+	if (tmp.num_counters)
+		get_counters(t->private->counters, counterstmp,
+		   t->private->nentries);
+
+	t->private = newinfo;
+	write_unlock_bh(&t->lock);
+	up(&ebt_mutex);
+	/* so, a user can change the chains while having messed up her counter
+	   allocation. Only reason why this is done is because this way the lock
+	   is held only once, while this doesn't bring the kernel into a
+	   dangerous state. */
+	if (tmp.num_counters &&
+	   copy_to_user(tmp.counters, counterstmp,
+	   tmp.num_counters * sizeof(struct ebt_counter))) {
+		BUGPRINT("Couldn't copy counters to userspace\n");
+		ret = -EFAULT;
+	}
+	else
+		ret = 0;
+
+	/* decrease module count and free resources */
+	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
+	   ebt_cleanup_entry, NULL);
+
+	vfree(table->entries);
+	if (table->chainstack) {
+		for (i = 0; i < NR_CPUS; i++)
+			vfree(table->chainstack[i]);
+		vfree(table->chainstack);
+	}
+	vfree(table);
+
+	if (counterstmp)
+		vfree(counterstmp);
+	return ret;
+
+free_unlock:
+	up(&ebt_mutex);
+free_iterate:
+	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+	   ebt_cleanup_entry, NULL);
+free_counterstmp:
+	if (counterstmp)
+		vfree(counterstmp);
+	/* can be initialized in translate_table() */
+	if (newinfo->chainstack) {
+		for (i = 0; i < NR_CPUS; i++)
+			vfree(newinfo->chainstack[i]);
+		vfree(newinfo->chainstack);
+	}
+free_entries:
+	if (newinfo->entries)
+		vfree(newinfo->entries);
+free_newinfo:
+	if (newinfo)
+		vfree(newinfo);
+	return ret;
+}
+
+int ebt_register_target(struct ebt_target *target)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_targets, target)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+
+	return 0;
+}
+
+void ebt_unregister_target(struct ebt_target *target)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_targets, target);
+	up(&ebt_mutex);
+}
+
+int ebt_register_match(struct ebt_match *match)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_matches, match)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+
+	return 0;
+}
+
+void ebt_unregister_match(struct ebt_match *match)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_matches, match);
+	up(&ebt_mutex);
+}
+
+int ebt_register_watcher(struct ebt_watcher *watcher)
+{
+	int ret;
+
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		return ret;
+	if (!list_named_insert(&ebt_watchers, watcher)) {
+		up(&ebt_mutex);
+		return -EEXIST;
+	}
+	up(&ebt_mutex);
+
+	return 0;
+}
+
+void ebt_unregister_watcher(struct ebt_watcher *watcher)
+{
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_watchers, watcher);
+	up(&ebt_mutex);
+}
+
+int ebt_register_table(struct ebt_table *table)
+{
+	struct ebt_table_info *newinfo;
+	int ret, i, countersize;
+
+	if (!table || !table->table ||!table->table->entries ||
+	    table->table->entries_size == 0 ||
+	    table->table->counters || table->private) {
+		BUGPRINT("Bad table data for ebt_register_table!!!\n");
+		return -EINVAL;
+	}
+
+	countersize = COUNTER_OFFSET(table->table->nentries) * NR_CPUS;
+	newinfo = (struct ebt_table_info *)
+	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+	ret = -ENOMEM;
+	if (!newinfo)
+		return -ENOMEM;
+
+	newinfo->entries = (char *)vmalloc(table->table->entries_size);
+	if (!(newinfo->entries))
+		goto free_newinfo;
+
+	memcpy(newinfo->entries, table->table->entries,
+	   table->table->entries_size);
+
+	if (countersize)
+		memset(newinfo->counters, 0, countersize);
+
+	/* fill in newinfo and parse the entries */
+	newinfo->chainstack = NULL;
+	ret = translate_table(table->table, newinfo);
+	if (ret != 0) {
+		BUGPRINT("Translate_table failed\n");
+		goto free_chainstack;
+	}
+
+	if (table->check && table->check(newinfo, table->valid_hooks)) {
+		BUGPRINT("The table doesn't like its own initial data, lol\n");
+		return -EINVAL;
+	}
+
+	table->private = newinfo;
+	table->lock = RW_LOCK_UNLOCKED;
+	ret = down_interruptible(&ebt_mutex);
+	if (ret != 0)
+		goto free_chainstack;
+
+	if (list_named_find(&ebt_tables, table->name)) {
+		ret = -EEXIST;
+		BUGPRINT("Table name already exists\n");
+		goto free_unlock;
+	}
+
+	/* Hold a reference count if the chains aren't empty */
+	if (newinfo->nentries && !try_module_get(table->me)) {
+		ret = -ENOENT;
+		goto free_unlock;
+	}
+	list_prepend(&ebt_tables, table);
+	up(&ebt_mutex);
+	return 0;
+free_unlock:
+	up(&ebt_mutex);
+free_chainstack:
+	if (newinfo->chainstack) {
+		for (i = 0; i < NR_CPUS; i++)
+			vfree(newinfo->chainstack[i]);
+		vfree(newinfo->chainstack);
+	}
+	vfree(newinfo->entries);
+free_newinfo:
+	vfree(newinfo);
+	return ret;
+}
+
+void ebt_unregister_table(struct ebt_table *table)
+{
+	int i;
+
+	if (!table) {
+		BUGPRINT("Request to unregister NULL table!!!\n");
+		return;
+	}
+	down(&ebt_mutex);
+	LIST_DELETE(&ebt_tables, table);
+	up(&ebt_mutex);
+	if (table->private->entries)
+		vfree(table->private->entries);
+	if (table->private->chainstack) {
+		for (i = 0; i < NR_CPUS; i++)
+			vfree(table->private->chainstack[i]);
+		vfree(table->private->chainstack);
+	}
+	vfree(table->private);
+}
+
+/* userspace just supplied us with counters */
+static int update_counters(void *user, unsigned int len)
+{
+	int i, ret;
+	struct ebt_counter *tmp;
+	struct ebt_replace hlp;
+	struct ebt_table *t;
+
+	if (copy_from_user(&hlp, user, sizeof(hlp)))
+		return -EFAULT;
+
+	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
+		return -EINVAL;
+	if (hlp.num_counters == 0)
+		return -EINVAL;
+
+	if ( !(tmp = (struct ebt_counter *)
+	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+		MEMPRINT("Update_counters && nomemory\n");
+		return -ENOMEM;
+	}
+
+	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+	if (!t)
+		goto free_tmp;
+
+	if (hlp.num_counters != t->private->nentries) {
+		BUGPRINT("Wrong nr of counters\n");
+		ret = -EINVAL;
+		goto unlock_mutex;
+	}
+
+	if ( copy_from_user(tmp, hlp.counters,
+	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
+		BUGPRINT("Updata_counters && !cfu\n");
+		ret = -EFAULT;
+		goto unlock_mutex;
+	}
+
+	/* we want an atomic add of the counters */
+	write_lock_bh(&t->lock);
+
+	/* we add to the counters of the first cpu */
+	for (i = 0; i < hlp.num_counters; i++) {
+		t->private->counters[i].pcnt += tmp[i].pcnt;
+		t->private->counters[i].bcnt += tmp[i].bcnt;
+	}
+
+	write_unlock_bh(&t->lock);
+	ret = 0;
+unlock_mutex:
+	up(&ebt_mutex);
+free_tmp:
+	vfree(tmp);
+	return ret;
+}
+
+static inline int ebt_make_matchname(struct ebt_entry_match *m,
+   char *base, char *ubase)
+{
+	char *hlp = ubase - base + (char *)m;
+	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
+   char *base, char *ubase)
+{
+	char *hlp = ubase - base + (char *)w;
+	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+{
+	int ret;
+	char *hlp;
+	struct ebt_entry_target *t;
+
+	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+		return 0;
+
+	hlp = ubase - base + (char *)e + e->target_offset;
+	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+	
+	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+	if (ret != 0)
+		return ret;
+	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
+	if (ret != 0)
+		return ret;
+	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+		return -EFAULT;
+	return 0;
+}
+
+/* called with ebt_mutex down */
+static int copy_everything_to_user(struct ebt_table *t, void *user,
+   int *len, int cmd)
+{
+	struct ebt_replace tmp;
+	struct ebt_counter *counterstmp, *oldcounters;
+	unsigned int entries_size, nentries;
+	char *entries;
+
+	if (cmd == EBT_SO_GET_ENTRIES) {
+		entries_size = t->private->entries_size;
+		nentries = t->private->nentries;
+		entries = t->private->entries;
+		oldcounters = t->private->counters;
+	} else {
+		entries_size = t->table->entries_size;
+		nentries = t->table->nentries;
+		entries = t->table->entries;
+		oldcounters = t->table->counters;
+	}
+
+	if (copy_from_user(&tmp, user, sizeof(tmp))) {
+		BUGPRINT("Cfu didn't work\n");
+		return -EFAULT;
+	}
+
+	if (*len != sizeof(struct ebt_replace) + entries_size +
+	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
+		BUGPRINT("Wrong size\n");
+		return -EINVAL;
+	}
+
+	if (tmp.nentries != nentries) {
+		BUGPRINT("Nentries wrong\n");
+		return -EINVAL;
+	}
+
+	if (tmp.entries_size != entries_size) {
+		BUGPRINT("Wrong size\n");
+		return -EINVAL;
+	}
+
+	/* userspace might not need the counters */
+	if (tmp.num_counters) {
+		if (tmp.num_counters != nentries) {
+			BUGPRINT("Num_counters wrong\n");
+			return -EINVAL;
+		}
+		counterstmp = (struct ebt_counter *)
+		   vmalloc(nentries * sizeof(struct ebt_counter));
+		if (!counterstmp) {
+			MEMPRINT("Couldn't copy counters, out of memory\n");
+			return -ENOMEM;
+		}
+		write_lock_bh(&t->lock);
+		get_counters(oldcounters, counterstmp, nentries);
+		write_unlock_bh(&t->lock);
+
+		if (copy_to_user(tmp.counters, counterstmp,
+		   nentries * sizeof(struct ebt_counter))) {
+			BUGPRINT("Couldn't copy counters to userspace\n");
+			vfree(counterstmp);
+			return -EFAULT;
+		}
+		vfree(counterstmp);
+	}
+
+	if (copy_to_user(tmp.entries, entries, entries_size)) {
+		BUGPRINT("Couldn't copy entries to userspace\n");
+		return -EFAULT;
+	}
+	/* set the match/watcher/target names right */
+	return EBT_ENTRY_ITERATE(entries, entries_size,
+	   ebt_make_names, entries, tmp.entries);
+}
+
+static int do_ebt_set_ctl(struct sock *sk,
+	int cmd, void *user, unsigned int len)
+{
+	int ret;
+
+	switch(cmd) {
+	case EBT_SO_SET_ENTRIES:
+		ret = do_replace(user, len);
+		break;
+	case EBT_SO_SET_COUNTERS:
+		ret = update_counters(user, len);
+		break;
+	default:
+		ret = -EINVAL;
+  }
+	return ret;
+}
+
+static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+	int ret;
+	struct ebt_replace tmp;
+	struct ebt_table *t;
+
+	if (copy_from_user(&tmp, user, sizeof(tmp)))
+		return -EFAULT;
+
+	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+	if (!t)
+		return ret;
+
+	switch(cmd) {
+	case EBT_SO_GET_INFO:
+	case EBT_SO_GET_INIT_INFO:
+		if (*len != sizeof(struct ebt_replace)){
+			ret = -EINVAL;
+			up(&ebt_mutex);
+			break;
+		}
+		if (cmd == EBT_SO_GET_INFO) {
+			tmp.nentries = t->private->nentries;
+			tmp.entries_size = t->private->entries_size;
+			tmp.valid_hooks = t->valid_hooks;
+		} else {
+			tmp.nentries = t->table->nentries;
+			tmp.entries_size = t->table->entries_size;
+			tmp.valid_hooks = t->table->valid_hooks;
+		}
+		up(&ebt_mutex);
+		if (copy_to_user(user, &tmp, *len) != 0){
+			BUGPRINT("c2u Didn't work\n");
+			ret = -EFAULT;
+			break;
+		}
+		ret = 0;
+		break;
+
+	case EBT_SO_GET_ENTRIES:
+	case EBT_SO_GET_INIT_ENTRIES:
+		ret = copy_everything_to_user(t, user, len, cmd);
+		up(&ebt_mutex);
+		break;
+
+	default:
+		up(&ebt_mutex);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct nf_sockopt_ops ebt_sockopts =
+{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
+    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+};
+
+static int __init init(void)
+{
+	int ret;
+
+	down(&ebt_mutex);
+	list_named_insert(&ebt_targets, &ebt_standard_target);
+	up(&ebt_mutex);
+	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+		return ret;
+
+	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	nf_unregister_sockopt(&ebt_sockopts);
+	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
+}
+
+EXPORT_SYMBOL(ebt_register_table);
+EXPORT_SYMBOL(ebt_unregister_table);
+EXPORT_SYMBOL(ebt_register_match);
+EXPORT_SYMBOL(ebt_unregister_match);
+EXPORT_SYMBOL(ebt_register_watcher);
+EXPORT_SYMBOL(ebt_unregister_watcher);
+EXPORT_SYMBOL(ebt_register_target);
+EXPORT_SYMBOL(ebt_unregister_target);
+EXPORT_SYMBOL(ebt_do_table);
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/kernel/linux2.5/net/core/dev.c b/kernel/linux2.5/net/core/dev.c
new file mode 100644
index 0000000..5efaf6e
--- /dev/null
+++ b/kernel/linux2.5/net/core/dev.c
@@ -0,0 +1,2872 @@
+/*
+ * 	NET3	Protocol independent device support routines.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ *	Derived from the non IP parts of dev.c 1.0.19
+ * 		Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *				Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ *	Additional Authors:
+ *		Florian la Roche <rzsfl@rz.uni-sb.de>
+ *		Alan Cox <gw4pts@gw4pts.ampr.org>
+ *		David Hinds <dhinds@allegro.stanford.edu>
+ *		Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *		Adam Sulmicki <adam@cfar.umd.edu>
+ *              Pekka Riikonen <priikone@poesidon.pspt.fi>
+ *
+ *	Changes:
+ *              D.J. Barrow     :       Fixed bug where dev->refcnt gets set
+ *              			to 2 if register_netdev gets called
+ *              			before net_dev_init & also removed a
+ *              			few lines of code in the process.
+ *		Alan Cox	:	device private ioctl copies fields back.
+ *		Alan Cox	:	Transmit queue code does relevant
+ *					stunts to keep the queue safe.
+ *		Alan Cox	:	Fixed double lock.
+ *		Alan Cox	:	Fixed promisc NULL pointer trap
+ *		????????	:	Support the full private ioctl range
+ *		Alan Cox	:	Moved ioctl permission check into
+ *					drivers
+ *		Tim Kordas	:	SIOCADDMULTI/SIOCDELMULTI
+ *		Alan Cox	:	100 backlog just doesn't cut it when
+ *					you start doing multicast video 8)
+ *		Alan Cox	:	Rewrote net_bh and list manager.
+ *		Alan Cox	: 	Fix ETH_P_ALL echoback lengths.
+ *		Alan Cox	:	Took out transmit every packet pass
+ *					Saved a few bytes in the ioctl handler
+ *		Alan Cox	:	Network driver sets packet type before
+ *					calling netif_rx. Saves a function
+ *					call a packet.
+ *		Alan Cox	:	Hashed net_bh()
+ *		Richard Kooijman:	Timestamp fixes.
+ *		Alan Cox	:	Wrong field in SIOCGIFDSTADDR
+ *		Alan Cox	:	Device lock protection.
+ *		Alan Cox	: 	Fixed nasty side effect of device close
+ *					changes.
+ *		Rudi Cilibrasi	:	Pass the right thing to
+ *					set_mac_address()
+ *		Dave Miller	:	32bit quantity for the device lock to
+ *					make it work out on a Sparc.
+ *		Bjorn Ekwall	:	Added KERNELD hack.
+ *		Alan Cox	:	Cleaned up the backlog initialise.
+ *		Craig Metz	:	SIOCGIFCONF fix if space for under
+ *					1 device.
+ *	    Thomas Bogendoerfer :	Return ENODEV for dev_open, if there
+ *					is no device open function.
+ *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
+ *	    Michael Chastain	:	Fix signed/unsigned for SIOCGIFCONF
+ *		Cyrus Durgin	:	Cleaned for KMOD
+ *		Adam Sulmicki   :	Bug Fix : Network Device Unload
+ *					A network device unload needs to purge
+ *					the backlog queue.
+ *	Paul Rusty Russell	:	SIOCSIFNAME
+ *              Pekka Riikonen  :	Netdev boot-time settings code
+ *              Andrew Morton   :       Make unregister_netdevice wait
+ *              			indefinitely on dev->refcnt
+ * 		J Hadi Salim	:	- Backlog queue sampling
+ *				        - netif_rx() feedback
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <linux/skbuff.h>
+#include <linux/brlock.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/if_bridge.h>
+#include <linux/divert.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <net/profile.h>
+#include <net/checksum.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
+#include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+#include <net/iw_handler.h>
+#endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
+
+
+/* This define, if set, will randomly drop a packet when congestion
+ * is more than moderate.  It helps fairness in the multi-interface
+ * case when one of them is a hog, but it kills performance for the
+ * single interface case so it is off now by default.
+ */
+#undef RAND_LIE
+
+/* Setting this will sample the queue lengths and thus congestion
+ * via a timer instead of as each packet is received.
+ */
+#undef OFFLINE_SAMPLE
+
+NET_PROFILE_DEFINE(dev_queue_xmit)
+NET_PROFILE_DEFINE(softnet_process)
+
+const char *if_port_text[] = {
+  "unknown",
+  "BNC",
+  "10baseT",
+  "AUI",
+  "100baseT",
+  "100baseTX",
+  "100baseFX"
+};
+
+/*
+ *	The list of packet types we will receive (as opposed to discard)
+ *	and the routines to invoke.
+ *
+ *	Why 16. Because with 16 the only overlap we get on a hash of the
+ *	low nibble of the protocol value is RARP/SNAP/X.25.
+ *
+ *      NOTE:  That is no longer true with the addition of VLAN tags.  Not
+ *             sure which should go first, but I bet it won't make much
+ *             difference if we are running VLANs.  The good news is that
+ *             this protocol won't be in the list unless compiled in, so
+ *             the average user (w/out VLANs) will not be adversly affected.
+ *             --BLG
+ *
+ *		0800	IP
+ *		8100    802.1Q VLAN
+ *		0001	802.3
+ *		0002	AX.25
+ *		0004	802.2
+ *		8035	RARP
+ *		0005	SNAP
+ *		0805	X.25
+ *		0806	ARP
+ *		8137	IPX
+ *		0009	Localtalk
+ *		86DD	IPv6
+ */
+
+static struct packet_type *ptype_base[16];	/* 16 way hashed list */
+static struct packet_type *ptype_all;		/* Taps */
+
+#ifdef OFFLINE_SAMPLE
+static void sample_queue(unsigned long dummy);
+static struct timer_list samp_timer = { function: sample_queue };
+#endif
+
+#ifdef CONFIG_HOTPLUG
+static int net_run_sbin_hotplug(struct net_device *dev, char *action);
+#else
+#define net_run_sbin_hotplug(dev, action) ({ 0; })
+#endif
+
+/*
+ *	Our notifier list
+ */
+
+static struct notifier_block *netdev_chain;
+
+/*
+ *	Device drivers call our routines to queue packets here. We empty the
+ *	queue in the local softnet handler.
+ */
+struct softnet_data softnet_data[NR_CPUS] __cacheline_aligned;
+
+#ifdef CONFIG_NET_FASTROUTE
+int netdev_fastroute;
+int netdev_fastroute_obstacles;
+#endif
+
+
+/*******************************************************************************
+
+		Protocol management and registration routines
+
+*******************************************************************************/
+
+/*
+ *	For efficiency
+ */
+
+int netdev_nit;
+
+/*
+ *	Add a protocol ID to the list. Now that the input handler is
+ *	smarter we can dispense with all the messy stuff that used to be
+ *	here.
+ *
+ *	BEWARE!!! Protocol handlers, mangling input packets,
+ *	MUST BE last in hash buckets and checking protocol handlers
+ *	MUST start from promiscous ptype_all chain in net_bh.
+ *	It is true now, do not change it.
+ *	Explantion follows: if protocol handler, mangling packet, will
+ *	be the first on list, it is not able to sense, that packet
+ *	is cloned and should be copied-on-write, so that it will
+ *	change it and subsequent readers will get broken packet.
+ *							--ANK (980803)
+ */
+
+/**
+ *	dev_add_pack - add packet handler
+ *	@pt: packet type declaration
+ *
+ *	Add a protocol handler to the networking stack. The passed &packet_type
+ *	is linked into kernel lists and may not be freed until it has been
+ *	removed from the kernel lists.
+ */
+
+void dev_add_pack(struct packet_type *pt)
+{
+	int hash;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+
+#ifdef CONFIG_NET_FASTROUTE
+	/* Hack to detect packet socket */
+	if (pt->data && (long)(pt->data) != 1) {
+		netdev_fastroute_obstacles++;
+		dev_clear_fastroute(pt->dev);
+	}
+#endif
+	if (pt->type == htons(ETH_P_ALL)) {
+		netdev_nit++;
+		pt->next  = ptype_all;
+		ptype_all = pt;
+	} else {
+		hash = ntohs(pt->type) & 15;
+		pt->next = ptype_base[hash];
+		ptype_base[hash] = pt;
+	}
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+
+/**
+ *	dev_remove_pack	 - remove packet handler
+ *	@pt: packet type declaration
+ *
+ *	Remove a protocol handler that was previously added to the kernel
+ *	protocol handlers by dev_add_pack(). The passed &packet_type is removed
+ *	from the kernel lists and can be freed or reused once this function
+ *	returns.
+ */
+void dev_remove_pack(struct packet_type *pt)
+{
+	struct packet_type **pt1;
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+
+	if (pt->type == htons(ETH_P_ALL)) {
+		netdev_nit--;
+		pt1 = &ptype_all;
+	} else
+		pt1 = &ptype_base[ntohs(pt->type) & 15];
+
+	for (; *pt1; pt1 = &((*pt1)->next)) {
+		if (pt == *pt1) {
+			*pt1 = pt->next;
+#ifdef CONFIG_NET_FASTROUTE
+			if (pt->data)
+				netdev_fastroute_obstacles--;
+#endif
+			goto out;
+		}
+	}
+	printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
+out:
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+/******************************************************************************
+
+		      Device Boot-time Settings Routines
+
+*******************************************************************************/
+
+/* Boot time configuration table */
+static struct netdev_boot_setup dev_boot_setup[NETDEV_BOOT_SETUP_MAX];
+
+/**
+ *	netdev_boot_setup_add	- add new setup entry
+ *	@name: name of the device
+ *	@map: configured settings for the device
+ *
+ *	Adds new setup entry to the dev_boot_setup list.  The function
+ *	returns 0 on error and 1 on success.  This is a generic routine to
+ *	all netdevices.
+ */
+int netdev_boot_setup_add(char *name, struct ifmap *map)
+{
+	struct netdev_boot_setup *s;
+	int i;
+
+	s = dev_boot_setup;
+	for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
+		if (s[i].name[0] == '\0' || s[i].name[0] == ' ') {
+			memset(s[i].name, 0, sizeof(s[i].name));
+			strcpy(s[i].name, name);
+			memcpy(&s[i].map, map, sizeof(s[i].map));
+			break;
+		}
+	}
+
+	return i >= NETDEV_BOOT_SETUP_MAX ? 0 : 1;
+}
+
+/**
+ *	netdev_boot_setup_check	- check boot time settings
+ *	@dev: the netdevice
+ *
+ * 	Check boot time settings for the device.
+ *	The found settings are set for the device to be used
+ *	later in the device probing.
+ *	Returns 0 if no settings found, 1 if they are.
+ */
+int netdev_boot_setup_check(struct net_device *dev)
+{
+	struct netdev_boot_setup *s = dev_boot_setup;
+	int i;
+
+	for (i = 0; i < NETDEV_BOOT_SETUP_MAX; i++) {
+		if (s[i].name[0] != '\0' && s[i].name[0] != ' ' &&
+		    !strncmp(dev->name, s[i].name, strlen(s[i].name))) {
+			dev->irq 	= s[i].map.irq;
+			dev->base_addr 	= s[i].map.base_addr;
+			dev->mem_start 	= s[i].map.mem_start;
+			dev->mem_end 	= s[i].map.mem_end;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Saves at boot time configured settings for any netdevice.
+ */
+int __init netdev_boot_setup(char *str)
+{
+	int ints[5];
+	struct ifmap map;
+
+	str = get_options(str, ARRAY_SIZE(ints), ints);
+	if (!str || !*str)
+		return 0;
+
+	/* Save settings */
+	memset(&map, 0, sizeof(map));
+	if (ints[0] > 0)
+		map.irq = ints[1];
+	if (ints[0] > 1)
+		map.base_addr = ints[2];
+	if (ints[0] > 2)
+		map.mem_start = ints[3];
+	if (ints[0] > 3)
+		map.mem_end = ints[4];
+
+	/* Add new entry to the list */
+	return netdev_boot_setup_add(str, &map);
+}
+
+__setup("netdev=", netdev_boot_setup);
+
+/*******************************************************************************
+
+			    Device Interface Subroutines
+
+*******************************************************************************/
+
+/**
+ *	__dev_get_by_name	- find a device by its name
+ *	@name: name to find
+ *
+ *	Find an interface by name. Must be called under RTNL semaphore
+ *	or @dev_base_lock. If the name is found a pointer to the device
+ *	is returned. If the name is not found then %NULL is returned. The
+ *	reference counters are not incremented so the caller must be
+ *	careful with locks.
+ */
+
+struct net_device *__dev_get_by_name(const char *name)
+{
+	struct net_device *dev;
+
+	for (dev = dev_base; dev; dev = dev->next)
+		if (!strncmp(dev->name, name, IFNAMSIZ))
+			break;
+	return dev;
+}
+
+/**
+ *	dev_get_by_name		- find a device by its name
+ *	@name: name to find
+ *
+ *	Find an interface by name. This can be called from any
+ *	context and does its own locking. The returned handle has
+ *	the usage count incremented and the caller must use dev_put() to
+ *	release it when it is no longer needed. %NULL is returned if no
+ *	matching device is found.
+ */
+
+struct net_device *dev_get_by_name(const char *name)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_name(name);
+	if (dev)
+		dev_hold(dev);
+	read_unlock(&dev_base_lock);
+	return dev;
+}
+
+/*
+   Return value is changed to int to prevent illegal usage in future.
+   It is still legal to use to check for device existence.
+
+   User should understand, that the result returned by this function
+   is meaningless, if it was not issued under rtnl semaphore.
+ */
+
+/**
+ *	dev_get	-	test if a device exists
+ *	@name:	name to test for
+ *
+ *	Test if a name exists. Returns true if the name is found. In order
+ *	to be sure the name is not allocated or removed during the test the
+ *	caller must hold the rtnl semaphore.
+ *
+ *	This function primarily exists for back compatibility with older
+ *	drivers.
+ */
+int dev_get(const char *name)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_name(name);
+	read_unlock(&dev_base_lock);
+	return dev != NULL;
+}
+
+/**
+ *	__dev_get_by_index - find a device by its ifindex
+ *	@ifindex: index of device
+ *
+ *	Search for an interface by index. Returns %NULL if the device
+ *	is not found or a pointer to the device. The device has not
+ *	had its reference counter increased so the caller must be careful
+ *	about locking. The caller must hold either the RTNL semaphore
+ *	or @dev_base_lock.
+ */
+
+struct net_device *__dev_get_by_index(int ifindex)
+{
+	struct net_device *dev;
+
+	for (dev = dev_base; dev; dev = dev->next)
+		if (dev->ifindex == ifindex)
+			break;
+	return dev;
+}
+
+
+/**
+ *	dev_get_by_index - find a device by its ifindex
+ *	@ifindex: index of device
+ *
+ *	Search for an interface by index. Returns NULL if the device
+ *	is not found or a pointer to the device. The device returned has
+ *	had a reference added and the pointer is safe until the user calls
+ *	dev_put to indicate they have finished with it.
+ */
+
+struct net_device *dev_get_by_index(int ifindex)
+{
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_index(ifindex);
+	if (dev)
+		dev_hold(dev);
+	read_unlock(&dev_base_lock);
+	return dev;
+}
+
+/**
+ *	dev_getbyhwaddr - find a device by its hardware address
+ *	@type: media type of device
+ *	@ha: hardware address
+ *
+ *	Search for an interface by MAC address. Returns NULL if the device
+ *	is not found or a pointer to the device. The caller must hold the
+ *	rtnl semaphore. The returned device has not had its ref count increased
+ *	and the caller must therefore be careful about locking
+ *
+ *	BUGS:
+ *	If the API was consistent this would be __dev_get_by_hwaddr
+ */
+
+struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
+{
+	struct net_device *dev;
+
+	ASSERT_RTNL();
+
+	for (dev = dev_base; dev; dev = dev->next)
+		if (dev->type == type &&
+		    !memcmp(dev->dev_addr, ha, dev->addr_len))
+			break;
+	return dev;
+}
+
+/**
+ *	dev_alloc_name - allocate a name for a device
+ *	@dev: device
+ *	@name: name format string
+ *
+ *	Passed a format string - eg "lt%d" it will try and find a suitable
+ *	id. Not efficient for many devices, not called a lot. The caller
+ *	must hold the dev_base or rtnl lock while allocating the name and
+ *	adding the device in order to avoid duplicates. Returns the number
+ *	of the unit assigned or a negative errno code.
+ */
+
+int dev_alloc_name(struct net_device *dev, const char *name)
+{
+	int i;
+	char buf[32];
+	char *p;
+
+	/*
+	 * Verify the string as this thing may have come from
+	 * the user.  There must be either one "%d" and no other "%"
+	 * characters, or no "%" characters at all.
+	 */
+	p = strchr(name, '%');
+	if (p && (p[1] != 'd' || strchr(p + 2, '%')))
+		return -EINVAL;
+
+	/*
+	 * If you need over 100 please also fix the algorithm...
+	 */
+	for (i = 0; i < 100; i++) {
+		snprintf(buf, sizeof(buf), name, i);
+		if (!__dev_get_by_name(buf)) {
+			strcpy(dev->name, buf);
+			return i;
+		}
+	}
+	return -ENFILE;	/* Over 100 of the things .. bail out! */
+}
+
+/**
+ *	dev_alloc - allocate a network device and name
+ *	@name: name format string
+ *	@err: error return pointer
+ *
+ *	Passed a format string, eg. "lt%d", it will allocate a network device
+ *	and space for the name. %NULL is returned if no memory is available.
+ *	If the allocation succeeds then the name is assigned and the
+ *	device pointer returned. %NULL is returned if the name allocation
+ *	failed. The cause of an error is returned as a negative errno code
+ *	in the variable @err points to.
+ *
+ *	The caller must hold the @dev_base or RTNL locks when doing this in
+ *	order to avoid duplicate name allocations.
+ */
+
+struct net_device *dev_alloc(const char *name, int *err)
+{
+	struct net_device *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+	if (!dev)
+		*err = -ENOBUFS;
+	else {
+		memset(dev, 0, sizeof(*dev));
+		*err = dev_alloc_name(dev, name);
+		if (*err < 0) {
+			kfree(dev);
+			dev = NULL;
+		}
+	}
+	return dev;
+}
+
+/**
+ *	netdev_state_change - device changes state
+ *	@dev: device to cause notification
+ *
+ *	Called to indicate a device has changed state. This function calls
+ *	the notifier chains for netdev_chain and sends a NEWLINK message
+ *	to the routing socket.
+ */
+void netdev_state_change(struct net_device *dev)
+{
+	if (dev->flags & IFF_UP) {
+		notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+		rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
+	}
+}
+
+
+#ifdef CONFIG_KMOD
+
+/**
+ *	dev_load 	- load a network module
+ *	@name: name of interface
+ *
+ *	If a network interface is not present and the process has suitable
+ *	privileges this function loads the module. If module loading is not
+ *	available in this kernel then it becomes a nop.
+ */
+
+void dev_load(const char *name)
+{
+	if (!dev_get(name) && capable(CAP_SYS_MODULE))
+		request_module(name);
+}
+
+#else
+
+extern inline void dev_load(const char *unused){;}
+
+#endif
+
+static int default_rebuild_header(struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n",
+	       skb->dev ? skb->dev->name : "NULL!!!");
+	kfree_skb(skb);
+	return 1;
+}
+
+/**
+ *	dev_open	- prepare an interface for use.
+ *	@dev:	device to open
+ *
+ *	Takes a device from down to up state. The device's private open
+ *	function is invoked and then the multicast lists are loaded. Finally
+ *	the device is moved into the up state and a %NETDEV_UP message is
+ *	sent to the netdev notifier chain.
+ *
+ *	Calling this function on an active interface is a nop. On a failure
+ *	a negative errno code is returned.
+ */
+int dev_open(struct net_device *dev)
+{
+	int ret = 0;
+
+	/*
+	 *	Is it already up?
+	 */
+
+	if (dev->flags & IFF_UP)
+		return 0;
+
+	/*
+	 *	Is it even present?
+	 */
+	if (!netif_device_present(dev))
+		return -ENODEV;
+
+	/*
+	 *	Call device private open method
+	 */
+	if (try_inc_mod_count(dev->owner)) {
+		set_bit(__LINK_STATE_START, &dev->state);
+		if (dev->open) {
+			ret = dev->open(dev);
+			if (ret) {
+				clear_bit(__LINK_STATE_START, &dev->state);
+				if (dev->owner)
+					__MOD_DEC_USE_COUNT(dev->owner);
+			}
+		}
+	} else {
+		ret = -ENODEV;
+	}
+
+	/*
+	 *	If it went open OK then:
+	 */
+
+	if (!ret) {
+		/*
+		 *	Set the flags.
+		 */
+		dev->flags |= IFF_UP;
+
+		/*
+		 *	Initialize multicasting status
+		 */
+		dev_mc_upload(dev);
+
+		/*
+		 *	Wakeup transmit queue engine
+		 */
+		dev_activate(dev);
+
+		/*
+		 *	... and announce new interface.
+		 */
+		notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+	}
+	return ret;
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+
+static void dev_do_clear_fastroute(struct net_device *dev)
+{
+	if (dev->accept_fastpath) {
+		int i;
+
+		for (i = 0; i <= NETDEV_FASTROUTE_HMASK; i++) {
+			struct dst_entry *dst;
+
+			write_lock_irq(&dev->fastpath_lock);
+			dst = dev->fastpath[i];
+			dev->fastpath[i] = NULL;
+			write_unlock_irq(&dev->fastpath_lock);
+
+			dst_release(dst);
+		}
+	}
+}
+
+void dev_clear_fastroute(struct net_device *dev)
+{
+	if (dev) {
+		dev_do_clear_fastroute(dev);
+	} else {
+		read_lock(&dev_base_lock);
+		for (dev = dev_base; dev; dev = dev->next)
+			dev_do_clear_fastroute(dev);
+		read_unlock(&dev_base_lock);
+	}
+}
+#endif
+
+/**
+ *	dev_close - shutdown an interface.
+ *	@dev: device to shutdown
+ *
+ *	This function moves an active device into down state. A
+ *	%NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
+ *	is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
+ *	chain.
+ */
+int dev_close(struct net_device *dev)
+{
+	if (!(dev->flags & IFF_UP))
+		return 0;
+
+	/*
+	 *	Tell people we are going down, so that they can
+	 *	prepare to death, when device is still operating.
+	 */
+	notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);
+
+	dev_deactivate(dev);
+
+	clear_bit(__LINK_STATE_START, &dev->state);
+
+	/* Synchronize to scheduled poll. We cannot touch poll list,
+	 * it can be even on different cpu. So just clear netif_running(),
+	 * and wait when poll really will happen. Actually, the best place
+	 * for this is inside dev->stop() after device stopped its irq
+	 * engine, but this requires more changes in devices. */
+
+	smp_mb__after_clear_bit(); /* Commit netif_running(). */
+	while (test_bit(__LINK_STATE_RX_SCHED, &dev->state)) {
+		/* No hurry. */
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+
+	/*
+	 *	Call the device specific close. This cannot fail.
+	 *	Only if device is UP
+	 *
+	 *	We allow it to be called even after a DETACH hot-plug
+	 *	event.
+	 */
+	if (dev->stop)
+		dev->stop(dev);
+
+	/*
+	 *	Device is now down.
+	 */
+
+	dev->flags &= ~IFF_UP;
+#ifdef CONFIG_NET_FASTROUTE
+	dev_clear_fastroute(dev);
+#endif
+
+	/*
+	 *	Tell people we are down
+	 */
+	notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+
+	/*
+	 * Drop the module refcount
+	 */
+	if (dev->owner)
+		__MOD_DEC_USE_COUNT(dev->owner);
+
+	return 0;
+}
+
+
+/*
+ *	Device change register/unregister. These are not inline or static
+ *	as we export them to the world.
+ */
+
+/**
+ *	register_netdevice_notifier - register a network notifier block
+ *	@nb: notifier
+ *
+ *	Register a notifier to be called when network device events occur.
+ *	The notifier passed is linked into the kernel structures and must
+ *	not be reused until it has been unregistered. A negative errno code
+ *	is returned on a failure.
+ */
+
+int register_netdevice_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_register(&netdev_chain, nb);
+}
+
+/**
+ *	unregister_netdevice_notifier - unregister a network notifier block
+ *	@nb: notifier
+ *
+ *	Unregister a notifier previously registered by
+ *	register_netdevice_notifier(). The notifier is unlinked into the
+ *	kernel structures and may then be reused. A negative errno code
+ *	is returned on a failure.
+ */
+
+int unregister_netdevice_notifier(struct notifier_block *nb)
+{
+	return notifier_chain_unregister(&netdev_chain, nb);
+}
+
+/*
+ *	Support routine. Sends outgoing frames to any network
+ *	taps currently in use.
+ */
+
+void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct packet_type *ptype;
+	do_gettimeofday(&skb->stamp);
+
+	br_read_lock(BR_NETPROTO_LOCK);
+	for (ptype = ptype_all; ptype; ptype = ptype->next) {
+		/* Never send packets back to the socket
+		 * they originated from - MvS (miquels@drinkel.ow.org)
+		 */
+		if ((ptype->dev == dev || !ptype->dev) &&
+		    (struct sock *)ptype->data != skb->sk) {
+			struct sk_buff *skb2= skb_clone(skb, GFP_ATOMIC);
+			if (!skb2)
+				break;
+
+			/* skb->nh should be correctly
+			   set by sender, so that the second statement is
+			   just protection against buggy protocols.
+			 */
+			skb2->mac.raw = skb2->data;
+
+			if (skb2->nh.raw < skb2->data ||
+			    skb2->nh.raw > skb2->tail) {
+				if (net_ratelimit())
+					printk(KERN_DEBUG "protocol %04x is "
+							  "buggy, dev %s\n",
+					       skb2->protocol, dev->name);
+				skb2->nh.raw = skb2->data;
+			}
+
+			skb2->h.raw = skb2->nh.raw;
+			skb2->pkt_type = PACKET_OUTGOING;
+			ptype->func(skb2, skb->dev, ptype);
+		}
+	}
+	br_read_unlock(BR_NETPROTO_LOCK);
+}
+
+/* Calculate csum in the case, when packet is misrouted.
+ * If it failed by some reason, ignore and send skb with wrong
+ * checksum.
+ */
+struct sk_buff *skb_checksum_help(struct sk_buff *skb)
+{
+	unsigned int csum;
+	int offset = skb->h.raw - skb->data;
+
+	if (offset > (int)skb->len)
+		BUG();
+	csum = skb_checksum(skb, offset, skb->len-offset, 0);
+
+	offset = skb->tail - skb->h.raw;
+	if (offset <= 0)
+		BUG();
+	if (skb->csum + 2 > offset)
+		BUG();
+
+	*(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
+	skb->ip_summed = CHECKSUM_NONE;
+	return skb;
+}
+
+#ifdef CONFIG_HIGHMEM
+/* Actually, we should eliminate this check as soon as we know, that:
+ * 1. IOMMU is present and allows to map all the memory.
+ * 2. No high memory really exists on this machine.
+ */
+
+static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
+{
+	int i;
+
+	if (dev->features & NETIF_F_HIGHDMA)
+		return 0;
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+		if (skb_shinfo(skb)->frags[i].page >= highmem_start_page)
+			return 1;
+
+	return 0;
+}
+#else
+#define illegal_highdma(dev, skb)	(0)
+#endif
+
+/**
+ *	dev_queue_xmit - transmit a buffer
+ *	@skb: buffer to transmit
+ *
+ *	Queue a buffer for transmission to a network device. The caller must
+ *	have set the device and priority and built the buffer before calling
+ *	this function. The function can be called from an interrupt.
+ *
+ *	A negative errno code is returned on a failure. A success does not
+ *	guarantee the frame will be transmitted as it may be dropped due
+ *	to congestion or traffic shaping.
+ */
+
+int dev_queue_xmit(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	struct Qdisc *q;
+	int rc = -ENOMEM;
+
+	if (skb_shinfo(skb)->frag_list &&
+	    !(dev->features & NETIF_F_FRAGLIST) &&
+	    skb_linearize(skb, GFP_ATOMIC))
+		goto out_kfree_skb;
+
+	/* Fragmented skb is linearized if device does not support SG,
+	 * or if at least one of fragments is in highmem and device
+	 * does not support DMA from it.
+	 */
+	if (skb_shinfo(skb)->nr_frags &&
+	    (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
+	    skb_linearize(skb, GFP_ATOMIC))
+		goto out_kfree_skb;
+
+	/* If packet is not checksummed and device does not support
+	 * checksumming for this protocol, complete checksumming here.
+	 */
+	if (skb->ip_summed == CHECKSUM_HW &&
+	    (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
+	     (!(dev->features & NETIF_F_IP_CSUM) ||
+	      skb->protocol != htons(ETH_P_IP)))) {
+		if ((skb = skb_checksum_help(skb)) == NULL)
+			goto out;
+	}
+
+	/* Grab device queue */
+	spin_lock_bh(&dev->queue_lock);
+	q = dev->qdisc;
+	if (q->enqueue) {
+		rc = q->enqueue(skb, q);
+
+		qdisc_run(dev);
+
+		spin_unlock_bh(&dev->queue_lock);
+		rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
+		goto out;
+	}
+
+	/* The device has no queue. Common case for software devices:
+	   loopback, all the sorts of tunnels...
+
+	   Really, it is unlikely that xmit_lock protection is necessary here.
+	   (f.e. loopback and IP tunnels are clean ignoring statistics
+	   counters.)
+	   However, it is possible, that they rely on protection
+	   made by us here.
+
+	   Check this and shot the lock. It is not prone from deadlocks.
+	   Either shot noqueue qdisc, it is even simpler 8)
+	 */
+	if (dev->flags & IFF_UP) {
+		int cpu = smp_processor_id();
+
+		if (dev->xmit_lock_owner != cpu) {
+			/*
+			 * The spin_lock effectivly does a preempt lock, but 
+			 * we are about to drop that...
+			 */
+			preempt_disable();
+			spin_unlock(&dev->queue_lock);
+			spin_lock(&dev->xmit_lock);
+			dev->xmit_lock_owner = cpu;
+			preempt_enable();
+
+			if (!netif_queue_stopped(dev)) {
+				if (netdev_nit)
+					dev_queue_xmit_nit(skb, dev);
+
+				rc = 0;
+				if (!dev->hard_start_xmit(skb, dev)) {
+					dev->xmit_lock_owner = -1;
+					spin_unlock_bh(&dev->xmit_lock);
+					goto out;
+				}
+			}
+			dev->xmit_lock_owner = -1;
+			spin_unlock_bh(&dev->xmit_lock);
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Virtual device %s asks to "
+				       "queue packet!\n", dev->name);
+			goto out_enetdown;
+		} else {
+			/* Recursion is detected! It is possible,
+			 * unfortunately */
+			if (net_ratelimit())
+				printk(KERN_DEBUG "Dead loop on virtual device "
+				       "%s, fix it urgently!\n", dev->name);
+		}
+	}
+	spin_unlock_bh(&dev->queue_lock);
+out_enetdown:
+	rc = -ENETDOWN;
+out_kfree_skb:
+	kfree_skb(skb);
+out:
+	return rc;
+}
+
+
+/*=======================================================================
+			Receiver routines
+  =======================================================================*/
+
+int netdev_max_backlog = 300;
+int weight_p = 64;            /* old backlog weight */
+/* These numbers are selected based on intuition and some
+ * experimentatiom, if you have more scientific way of doing this
+ * please go ahead and fix things.
+ */
+int no_cong_thresh = 10;
+int no_cong = 20;
+int lo_cong = 100;
+int mod_cong = 290;
+
+struct netif_rx_stats netdev_rx_stat[NR_CPUS];
+
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+atomic_t netdev_dropping = ATOMIC_INIT(0);
+static unsigned long netdev_fc_mask = 1;
+unsigned long netdev_fc_xoff;
+spinlock_t netdev_fc_lock = SPIN_LOCK_UNLOCKED;
+
+static struct
+{
+	void (*stimul)(struct net_device *);
+	struct net_device *dev;
+} netdev_fc_slots[BITS_PER_LONG];
+
+int netdev_register_fc(struct net_device *dev,
+		       void (*stimul)(struct net_device *dev))
+{
+	int bit = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&netdev_fc_lock, flags);
+	if (netdev_fc_mask != ~0UL) {
+		bit = ffz(netdev_fc_mask);
+		netdev_fc_slots[bit].stimul = stimul;
+		netdev_fc_slots[bit].dev = dev;
+		set_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	spin_unlock_irqrestore(&netdev_fc_lock, flags);
+	return bit;
+}
+
+void netdev_unregister_fc(int bit)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&netdev_fc_lock, flags);
+	if (bit > 0) {
+		netdev_fc_slots[bit].stimul = NULL;
+		netdev_fc_slots[bit].dev = NULL;
+		clear_bit(bit, &netdev_fc_mask);
+		clear_bit(bit, &netdev_fc_xoff);
+	}
+	spin_unlock_irqrestore(&netdev_fc_lock, flags);
+}
+
+static void netdev_wakeup(void)
+{
+	unsigned long xoff;
+
+	spin_lock(&netdev_fc_lock);
+	xoff = netdev_fc_xoff;
+	netdev_fc_xoff = 0;
+	while (xoff) {
+		int i = ffz(~xoff);
+		xoff &= ~(1 << i);
+		netdev_fc_slots[i].stimul(netdev_fc_slots[i].dev);
+	}
+	spin_unlock(&netdev_fc_lock);
+}
+#endif
+
+static void get_sample_stats(int cpu)
+{
+#ifdef RAND_LIE
+	unsigned long rd;
+	int rq;
+#endif
+	int blog = softnet_data[cpu].input_pkt_queue.qlen;
+	int avg_blog = softnet_data[cpu].avg_blog;
+
+	avg_blog = (avg_blog >> 1) + (blog >> 1);
+
+	if (avg_blog > mod_cong) {
+		/* Above moderate congestion levels. */
+		softnet_data[cpu].cng_level = NET_RX_CN_HIGH;
+#ifdef RAND_LIE
+		rd = net_random();
+		rq = rd % netdev_max_backlog;
+		if (rq < avg_blog) /* unlucky bastard */
+			softnet_data[cpu].cng_level = NET_RX_DROP;
+#endif
+	} else if (avg_blog > lo_cong) {
+		softnet_data[cpu].cng_level = NET_RX_CN_MOD;
+#ifdef RAND_LIE
+		rd = net_random();
+		rq = rd % netdev_max_backlog;
+			if (rq < avg_blog) /* unlucky bastard */
+				softnet_data[cpu].cng_level = NET_RX_CN_HIGH;
+#endif
+	} else if (avg_blog > no_cong)
+		softnet_data[cpu].cng_level = NET_RX_CN_LOW;
+	else  /* no congestion */
+		softnet_data[cpu].cng_level = NET_RX_SUCCESS;
+
+	softnet_data[cpu].avg_blog = avg_blog;
+}
+
+#ifdef OFFLINE_SAMPLE
+static void sample_queue(unsigned long dummy)
+{
+/* 10 ms 0r 1ms -- i dont care -- JHS */
+	int next_tick = 1;
+	int cpu = smp_processor_id();
+
+	get_sample_stats(cpu);
+	next_tick += jiffies;
+	mod_timer(&samp_timer, next_tick);
+}
+#endif
+
+
+/**
+ *	netif_rx	-	post buffer to the network code
+ *	@skb: buffer to post
+ *
+ *	This function receives a packet from a device driver and queues it for
+ *	the upper (protocol) levels to process.  It always succeeds. The buffer
+ *	may be dropped during processing for congestion control or by the
+ *	protocol layers.
+ *
+ *	return values:
+ *	NET_RX_SUCCESS	(no congestion)
+ *	NET_RX_CN_LOW   (low congestion)
+ *	NET_RX_CN_MOD   (moderate congestion)
+ *	NET_RX_CN_HIGH  (high congestion)
+ *	NET_RX_DROP     (packet was dropped)
+ *
+ */
+
+int netif_rx(struct sk_buff *skb)
+{
+	int this_cpu;
+	struct softnet_data *queue;
+	unsigned long flags;
+
+	if (!skb->stamp.tv_sec)
+		do_gettimeofday(&skb->stamp);
+
+	/*
+	 * The code is rearranged so that the path is the most
+	 * short when CPU is congested, but is still operating.
+	 */
+	local_irq_save(flags);
+	this_cpu = smp_processor_id();
+	queue = &softnet_data[this_cpu];
+
+	netdev_rx_stat[this_cpu].total++;
+	if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
+		if (queue->input_pkt_queue.qlen) {
+			if (queue->throttle)
+				goto drop;
+
+enqueue:
+			dev_hold(skb->dev);
+			__skb_queue_tail(&queue->input_pkt_queue, skb);
+#ifndef OFFLINE_SAMPLE
+			get_sample_stats(this_cpu);
+#endif
+			local_irq_restore(flags);
+			return queue->cng_level;
+		}
+
+		if (queue->throttle) {
+			queue->throttle = 0;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+			if (atomic_dec_and_test(&netdev_dropping))
+				netdev_wakeup();
+#endif
+		}
+
+		netif_rx_schedule(&queue->backlog_dev);
+		goto enqueue;
+	}
+
+	if (!queue->throttle) {
+		queue->throttle = 1;
+		netdev_rx_stat[this_cpu].throttled++;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		atomic_inc(&netdev_dropping);
+#endif
+	}
+
+drop:
+	netdev_rx_stat[this_cpu].dropped++;
+	local_irq_restore(flags);
+
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/* Deliver skb to an old protocol, which is not threaded well
+   or which do not understand shared skbs.
+ */
+static int deliver_to_old_ones(struct packet_type *pt,
+			       struct sk_buff *skb, int last)
+{
+	static spinlock_t net_bh_lock = SPIN_LOCK_UNLOCKED;
+	int ret = NET_RX_DROP;
+
+	if (!last) {
+		skb = skb_clone(skb, GFP_ATOMIC);
+		if (!skb)
+			goto out;
+	}
+	if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC))
+		goto out_kfree;
+
+	/* The assumption (correct one) is that old protocols
+	   did not depened on BHs different of NET_BH and TIMER_BH.
+	 */
+
+	/* Emulate NET_BH with special spinlock */
+	spin_lock(&net_bh_lock);
+
+	/* Disable timers and wait for all timers completion */
+	tasklet_disable(bh_task_vec+TIMER_BH);
+
+	ret = pt->func(skb, skb->dev, pt);
+
+	tasklet_hi_enable(bh_task_vec+TIMER_BH);
+	spin_unlock(&net_bh_lock);
+out:
+	return ret;
+out_kfree:
+	kfree_skb(skb);
+	goto out;
+}
+
+static __inline__ void skb_bond(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+
+	if (dev->master)
+		skb->dev = dev->master;
+}
+
+static void net_tx_action(struct softirq_action *h)
+{
+	int cpu = smp_processor_id();
+
+	if (softnet_data[cpu].completion_queue) {
+		struct sk_buff *clist;
+
+		local_irq_disable();
+		clist = softnet_data[cpu].completion_queue;
+		softnet_data[cpu].completion_queue = NULL;
+		local_irq_enable();
+
+		while (clist) {
+			struct sk_buff *skb = clist;
+			clist = clist->next;
+
+			BUG_TRAP(!atomic_read(&skb->users));
+			__kfree_skb(skb);
+		}
+	}
+
+	if (softnet_data[cpu].output_queue) {
+		struct net_device *head;
+
+		local_irq_disable();
+		head = softnet_data[cpu].output_queue;
+		softnet_data[cpu].output_queue = NULL;
+		local_irq_enable();
+
+		while (head) {
+			struct net_device *dev = head;
+			head = head->next_sched;
+
+			smp_mb__before_clear_bit();
+			clear_bit(__LINK_STATE_SCHED, &dev->state);
+
+			if (spin_trylock(&dev->queue_lock)) {
+				qdisc_run(dev);
+				spin_unlock(&dev->queue_lock);
+			} else {
+				netif_schedule(dev);
+			}
+		}
+	}
+}
+
+/**
+ *	net_call_rx_atomic
+ *	@fn: function to call
+ *
+ *	Make a function call that is atomic with respect to the protocol
+ *	layers.
+ */
+void net_call_rx_atomic(void (*fn)(void))
+{
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	fn();
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+}
+
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+#endif
+
+static __inline__ int handle_bridge(struct sk_buff *skb,
+				     struct packet_type *pt_prev)
+{
+	int ret = NET_RX_DROP;
+
+	if (pt_prev) {
+		if (!pt_prev->data)
+			ret = deliver_to_old_ones(pt_prev, skb, 0);
+		else {
+			atomic_inc(&skb->users);
+			ret = pt_prev->func(skb, skb->dev, pt_prev);
+		}
+	}
+
+	return ret;
+}
+
+
+#ifdef CONFIG_NET_DIVERT
+static inline int handle_diverter(struct sk_buff *skb)
+{
+	/* if diversion is supported on device, then divert */
+	if (skb->dev->divert && skb->dev->divert->divert)
+		divert_frame(skb);
+	return 0;
+}
+#endif   /* CONFIG_NET_DIVERT */
+
+int netif_receive_skb(struct sk_buff *skb)
+{
+	struct packet_type *ptype, *pt_prev;
+	int ret = NET_RX_DROP;
+	unsigned short type = skb->protocol;
+
+	if (!skb->stamp.tv_sec)
+		do_gettimeofday(&skb->stamp);
+
+	skb_bond(skb);
+
+	netdev_rx_stat[smp_processor_id()].total++;
+
+#ifdef CONFIG_NET_FASTROUTE
+	if (skb->pkt_type == PACKET_FASTROUTE) {
+		netdev_rx_stat[smp_processor_id()].fastroute_deferred_out++;
+		return dev_queue_xmit(skb);
+	}
+#endif
+
+	skb->h.raw = skb->nh.raw = skb->data;
+
+	pt_prev = NULL;
+	for (ptype = ptype_all; ptype; ptype = ptype->next) {
+		if (!ptype->dev || ptype->dev == skb->dev) {
+			if (pt_prev) {
+				if (!pt_prev->data) {
+					ret = deliver_to_old_ones(pt_prev,
+								  skb, 0);
+				} else {
+					atomic_inc(&skb->users);
+					ret = pt_prev->func(skb, skb->dev,
+							    pt_prev);
+				}
+			}
+			pt_prev = ptype;
+		}
+	}
+
+#ifdef CONFIG_NET_DIVERT
+	if (skb->dev->divert && skb->dev->divert->divert)
+		ret = handle_diverter(skb);
+#endif /* CONFIG_NET_DIVERT */
+
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+	if (skb->dev->br_port && br_handle_frame_hook) {
+		int ret;
+
+		ret = handle_bridge(skb, pt_prev);
+		if (br_handle_frame_hook(skb) == 0)
+			return ret;
+		pt_prev = NULL;
+	}
+#endif
+
+	for (ptype = ptype_base[ntohs(type) & 15]; ptype; ptype = ptype->next) {
+		if (ptype->type == type &&
+		    (!ptype->dev || ptype->dev == skb->dev)) {
+			if (pt_prev) {
+				if (!pt_prev->data) {
+					ret = deliver_to_old_ones(pt_prev,
+								  skb, 0);
+				} else {
+					atomic_inc(&skb->users);
+					ret = pt_prev->func(skb, skb->dev,
+							    pt_prev);
+				}
+			}
+			pt_prev = ptype;
+		}
+	}
+
+	if (pt_prev) {
+		if (!pt_prev->data) {
+			ret = deliver_to_old_ones(pt_prev, skb, 1);
+		} else {
+			ret = pt_prev->func(skb, skb->dev, pt_prev);
+		}
+	} else {
+		kfree_skb(skb);
+		/* Jamal, now you will not able to escape explaining
+		 * me how you were going to use this. :-)
+		 */
+		ret = NET_RX_DROP;
+	}
+
+	return ret;
+}
+
+static int process_backlog(struct net_device *backlog_dev, int *budget)
+{
+	int work = 0;
+	int quota = min(backlog_dev->quota, *budget);
+	int this_cpu = smp_processor_id();
+	struct softnet_data *queue = &softnet_data[this_cpu];
+	unsigned long start_time = jiffies;
+
+	for (;;) {
+		struct sk_buff *skb;
+		struct net_device *dev;
+
+		local_irq_disable();
+		skb = __skb_dequeue(&queue->input_pkt_queue);
+		if (!skb)
+			goto job_done;
+		local_irq_enable();
+
+		dev = skb->dev;
+
+		netif_receive_skb(skb);
+
+		dev_put(dev);
+
+		work++;
+
+		if (work >= quota || jiffies - start_time > 1)
+			break;
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (queue->throttle &&
+		    queue->input_pkt_queue.qlen < no_cong_thresh ) {
+			if (atomic_dec_and_test(&netdev_dropping)) {
+				queue->throttle = 0;
+				netdev_wakeup();
+				break;
+			}
+		}
+#endif
+	}
+
+	backlog_dev->quota -= work;
+	*budget -= work;
+	return -1;
+
+job_done:
+	backlog_dev->quota -= work;
+	*budget -= work;
+
+	list_del(&backlog_dev->poll_list);
+	clear_bit(__LINK_STATE_RX_SCHED, &backlog_dev->state);
+
+	if (queue->throttle) {
+		queue->throttle = 0;
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+		if (atomic_dec_and_test(&netdev_dropping))
+			netdev_wakeup();
+#endif
+	}
+	local_irq_enable();
+	return 0;
+}
+
+static void net_rx_action(struct softirq_action *h)
+{
+	int this_cpu = smp_processor_id();
+	struct softnet_data *queue = &softnet_data[this_cpu];
+	unsigned long start_time = jiffies;
+	int budget = netdev_max_backlog;
+
+	br_read_lock(BR_NETPROTO_LOCK);
+	local_irq_disable();
+
+	while (!list_empty(&queue->poll_list)) {
+		struct net_device *dev;
+
+		if (budget <= 0 || jiffies - start_time > 1)
+			goto softnet_break;
+
+		local_irq_enable();
+
+		dev = list_entry(queue->poll_list.next,
+				 struct net_device, poll_list);
+
+		if (dev->quota <= 0 || dev->poll(dev, &budget)) {
+			local_irq_disable();
+			list_del(&dev->poll_list);
+			list_add_tail(&dev->poll_list, &queue->poll_list);
+			if (dev->quota < 0)
+				dev->quota += dev->weight;
+			else
+				dev->quota = dev->weight;
+		} else {
+			dev_put(dev);
+			local_irq_disable();
+		}
+	}
+out:
+	local_irq_enable();
+	br_read_unlock(BR_NETPROTO_LOCK);
+	return;
+
+softnet_break:
+	netdev_rx_stat[this_cpu].time_squeeze++;
+	__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);
+	goto out;
+}
+
+static gifconf_func_t * gifconf_list [NPROTO];
+
+/**
+ *	register_gifconf	-	register a SIOCGIF handler
+ *	@family: Address family
+ *	@gifconf: Function handler
+ *
+ *	Register protocol dependent address dumping routines. The handler
+ *	that is passed must not be freed or reused until it has been replaced
+ *	by another handler.
+ */
+int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
+{
+	if (family >= NPROTO)
+		return -EINVAL;
+	gifconf_list[family] = gifconf;
+	return 0;
+}
+
+
+/*
+ *	Map an interface index to its name (SIOCGIFNAME)
+ */
+
+/*
+ *	We need this ioctl for efficient implementation of the
+ *	if_indextoname() function required by the IPv6 API.  Without
+ *	it, we would have to search all the interfaces to find a
+ *	match.  --pb
+ */
+
+static int dev_ifname(struct ifreq *arg)
+{
+	struct net_device *dev;
+	struct ifreq ifr;
+
+	/*
+	 *	Fetch the caller's info block.
+	 */
+
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
+
+	read_lock(&dev_base_lock);
+	dev = __dev_get_by_index(ifr.ifr_ifindex);
+	if (!dev) {
+		read_unlock(&dev_base_lock);
+		return -ENODEV;
+	}
+
+	strcpy(ifr.ifr_name, dev->name);
+	read_unlock(&dev_base_lock);
+
+	if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ *	Perform a SIOCGIFCONF call. This structure will change
+ *	size eventually, and there is nothing I can do about it.
+ *	Thus we will need a 'compatibility mode'.
+ */
+
+static int dev_ifconf(char *arg)
+{
+	struct ifconf ifc;
+	struct net_device *dev;
+	char *pos;
+	int len;
+	int total;
+	int i;
+
+	/*
+	 *	Fetch the caller's info block.
+	 */
+
+	if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
+		return -EFAULT;
+
+	pos = ifc.ifc_buf;
+	len = ifc.ifc_len;
+
+	/*
+	 *	Loop over the interfaces, and write an info block for each.
+	 */
+
+	total = 0;
+	for (dev = dev_base; dev; dev = dev->next) {
+		for (i = 0; i < NPROTO; i++) {
+			if (gifconf_list[i]) {
+				int done;
+				if (!pos)
+					done = gifconf_list[i](dev, NULL, 0);
+				else
+					done = gifconf_list[i](dev, pos + total,
+							       len - total);
+				if (done < 0)
+					return -EFAULT;
+				total += done;
+			}
+		}
+  	}
+
+	/*
+	 *	All done.  Write the updated control block back to the caller.
+	 */
+	ifc.ifc_len = total;
+
+	/*
+	 * 	Both BSD and Solaris return 0 here, so we do too.
+	 */
+	return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0;
+}
+
+/*
+ *	This is invoked by the /proc filesystem handler to display a device
+ *	in detail.
+ */
+
+#ifdef CONFIG_PROC_FS
+
+static int sprintf_stats(char *buffer, struct net_device *dev)
+{
+	struct net_device_stats *stats = dev->get_stats ? dev->get_stats(dev) :
+							  NULL;
+	int size;
+
+	if (stats)
+		size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu "
+				       "%10lu %9lu %8lu %7lu %4lu %4lu %4lu "
+				       "%5lu %7lu %10lu\n",
+ 		   dev->name,
+		   stats->rx_bytes,
+		   stats->rx_packets, stats->rx_errors,
+		   stats->rx_dropped + stats->rx_missed_errors,
+		   stats->rx_fifo_errors,
+		   stats->rx_length_errors + stats->rx_over_errors +
+		   	stats->rx_crc_errors + stats->rx_frame_errors,
+		   stats->rx_compressed, stats->multicast,
+		   stats->tx_bytes,
+		   stats->tx_packets, stats->tx_errors, stats->tx_dropped,
+		   stats->tx_fifo_errors, stats->collisions,
+		   stats->tx_carrier_errors + stats->tx_aborted_errors +
+		   	stats->tx_window_errors + stats->tx_heartbeat_errors,
+		   stats->tx_compressed);
+	else
+		size = sprintf(buffer, "%6s: No statistics available.\n",
+			       dev->name);
+
+	return size;
+}
+
+/*
+ *	Called from the PROCfs module. This now uses the new arbitrary sized
+ *	/proc/net interface to create /proc/net/dev
+ */
+static int dev_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	int len = 0;
+	off_t begin = 0;
+	off_t pos = 0;
+	int size;
+	struct net_device *dev;
+
+	size = sprintf(buffer,
+		"Inter-|   Receive                                                |  Transmit\n"
+		" face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n");
+
+	pos += size;
+	len += size;
+
+	read_lock(&dev_base_lock);
+	for (dev = dev_base; dev; dev = dev->next) {
+		size = sprintf_stats(buffer+len, dev);
+		len += size;
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+		if (pos > offset + length)
+			break;
+	}
+	read_unlock(&dev_base_lock);
+
+	*start = buffer + (offset - begin);	/* Start of wanted data */
+	len -= offset - begin;			/* Start slop */
+	if (len > length)
+		len = length;			/* Ending slop */
+	if (len < 0)
+		len = 0;
+	return len;
+}
+
+static int dev_proc_stats(char *buffer, char **start, off_t offset,
+			  int length, int *eof, void *data)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		if (!cpu_online(i))
+			continue;
+
+		len += sprintf(buffer + len, "%08x %08x %08x %08x %08x %08x "
+					     "%08x %08x %08x\n",
+			       netdev_rx_stat[i].total,
+			       netdev_rx_stat[i].dropped,
+			       netdev_rx_stat[i].time_squeeze,
+			       netdev_rx_stat[i].throttled,
+			       netdev_rx_stat[i].fastroute_hit,
+			       netdev_rx_stat[i].fastroute_success,
+			       netdev_rx_stat[i].fastroute_defer,
+			       netdev_rx_stat[i].fastroute_deferred_out,
+#if 0
+			       netdev_rx_stat[i].fastroute_latency_reduction
+#else
+			       netdev_rx_stat[i].cpu_collision
+#endif
+			       );
+	}
+
+	len -= offset;
+
+	if (len > length)
+		len = length;
+	if (len < 0)
+		len = 0;
+
+	*start = buffer + offset;
+	*eof = 1;
+
+	return len;
+}
+
+#endif	/* CONFIG_PROC_FS */
+
+
+/**
+ *	netdev_set_master	-	set up master/slave pair
+ *	@slave: slave device
+ *	@master: new master device
+ *
+ *	Changes the master device of the slave. Pass %NULL to break the
+ *	bonding. The caller must hold the RTNL semaphore. On a failure
+ *	a negative errno code is returned. On success the reference counts
+ *	are adjusted, %RTM_NEWLINK is sent to the routing socket and the
+ *	function returns zero.
+ */
+int netdev_set_master(struct net_device *slave, struct net_device *master)
+{
+	struct net_device *old = slave->master;
+
+	ASSERT_RTNL();
+
+	if (master) {
+		if (old)
+			return -EBUSY;
+		dev_hold(master);
+	}
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	slave->master = master;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+	if (old)
+		dev_put(old);
+
+	if (master)
+		slave->flags |= IFF_SLAVE;
+	else
+		slave->flags &= ~IFF_SLAVE;
+
+	rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
+	return 0;
+}
+
+/**
+ *	dev_set_promiscuity	- update promiscuity count on a device
+ *	@dev: device
+ *	@inc: modifier
+ *
+ *	Add or remove promsicuity from a device. While the count in the device
+ *	remains above zero the interface remains promiscuous. Once it hits zero
+ *	the device reverts back to normal filtering operation. A negative inc
+ *	value is used to drop promiscuity on the device.
+ */
+void dev_set_promiscuity(struct net_device *dev, int inc)
+{
+	unsigned short old_flags = dev->flags;
+
+	dev->flags |= IFF_PROMISC;
+	if ((dev->promiscuity += inc) == 0)
+		dev->flags &= ~IFF_PROMISC;
+	if (dev->flags ^ old_flags) {
+#ifdef CONFIG_NET_FASTROUTE
+		if (dev->flags & IFF_PROMISC) {
+			netdev_fastroute_obstacles++;
+			dev_clear_fastroute(dev);
+		} else
+			netdev_fastroute_obstacles--;
+#endif
+		dev_mc_upload(dev);
+		printk(KERN_INFO "device %s %s promiscuous mode\n",
+		       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
+		       					       "left");
+	}
+}
+
+/**
+ *	dev_set_allmulti	- update allmulti count on a device
+ *	@dev: device
+ *	@inc: modifier
+ *
+ *	Add or remove reception of all multicast frames to a device. While the
+ *	count in the device remains above zero the interface remains listening
+ *	to all interfaces. Once it hits zero the device reverts back to normal
+ *	filtering operation. A negative @inc value is used to drop the counter
+ *	when releasing a resource needing all multicasts.
+ */
+
+void dev_set_allmulti(struct net_device *dev, int inc)
+{
+	unsigned short old_flags = dev->flags;
+
+	dev->flags |= IFF_ALLMULTI;
+	if ((dev->allmulti += inc) == 0)
+		dev->flags &= ~IFF_ALLMULTI;
+	if (dev->flags ^ old_flags)
+		dev_mc_upload(dev);
+}
+
+int dev_change_flags(struct net_device *dev, unsigned flags)
+{
+	int ret;
+	int old_flags = dev->flags;
+
+	/*
+	 *	Set the flags on our device.
+	 */
+
+	dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
+			       IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
+			       IFF_AUTOMEDIA)) |
+		     (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
+				    IFF_ALLMULTI));
+
+	/*
+	 *	Load in the correct multicast list now the flags have changed.
+	 */
+
+	dev_mc_upload(dev);
+
+	/*
+	 *	Have we downed the interface. We handle IFF_UP ourselves
+	 *	according to user attempts to set it, rather than blindly
+	 *	setting it.
+	 */
+
+	ret = 0;
+	if ((old_flags ^ flags) & IFF_UP) {	/* Bit is different  ? */
+		ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
+
+		if (!ret)
+			dev_mc_upload(dev);
+	}
+
+	if (dev->flags & IFF_UP &&
+	    ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
+					  IFF_VOLATILE)))
+		notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+
+	if ((flags ^ dev->gflags) & IFF_PROMISC) {
+		int inc = (flags & IFF_PROMISC) ? +1 : -1;
+		dev->gflags ^= IFF_PROMISC;
+		dev_set_promiscuity(dev, inc);
+	}
+
+	/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
+	   is important. Some (broken) drivers set IFF_PROMISC, when
+	   IFF_ALLMULTI is requested not asking us and not reporting.
+	 */
+	if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
+		int inc = (flags & IFF_ALLMULTI) ? +1 : -1;
+		dev->gflags ^= IFF_ALLMULTI;
+		dev_set_allmulti(dev, inc);
+	}
+
+	if (old_flags ^ dev->flags)
+		rtmsg_ifinfo(RTM_NEWLINK, dev, old_flags ^ dev->flags);
+
+	return ret;
+}
+
+/*
+ *	Perform the SIOCxIFxxx calls.
+ */
+static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
+{
+	int err;
+	struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
+
+	if (!dev)
+		return -ENODEV;
+
+	switch (cmd) {
+		case SIOCGIFFLAGS:	/* Get interface flags */
+			ifr->ifr_flags = (dev->flags & ~(IFF_PROMISC |
+							 IFF_ALLMULTI |
+							 IFF_RUNNING)) | 
+					 (dev->gflags & (IFF_PROMISC |
+							 IFF_ALLMULTI));
+			if (netif_running(dev) && netif_carrier_ok(dev))
+				ifr->ifr_flags |= IFF_RUNNING;
+			return 0;
+
+		case SIOCSIFFLAGS:	/* Set interface flags */
+			return dev_change_flags(dev, ifr->ifr_flags);
+
+		case SIOCGIFMETRIC:	/* Get the metric on the interface
+					   (currently unused) */
+			ifr->ifr_metric = 0;
+			return 0;
+
+		case SIOCSIFMETRIC:	/* Set the metric on the interface
+					   (currently unused) */
+			return -EOPNOTSUPP;
+
+		case SIOCGIFMTU:	/* Get the MTU of a device */
+			ifr->ifr_mtu = dev->mtu;
+			return 0;
+
+		case SIOCSIFMTU:	/* Set the MTU of a device */
+			if (ifr->ifr_mtu == dev->mtu)
+				return 0;
+
+			/*
+			 *	MTU must be positive.
+			 */
+			if (ifr->ifr_mtu < 0)
+				return -EINVAL;
+
+			if (!netif_device_present(dev))
+				return -ENODEV;
+
+			err = 0;
+			if (dev->change_mtu)
+				err = dev->change_mtu(dev, ifr->ifr_mtu);
+			else
+				dev->mtu = ifr->ifr_mtu;
+			if (!err && dev->flags & IFF_UP)
+				notifier_call_chain(&netdev_chain,
+						    NETDEV_CHANGEMTU, dev);
+			return err;
+
+		case SIOCGIFHWADDR:
+			memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
+			       MAX_ADDR_LEN);
+			ifr->ifr_hwaddr.sa_family = dev->type;
+			return 0;
+
+		case SIOCSIFHWADDR:
+			if (!dev->set_mac_address)
+				return -EOPNOTSUPP;
+			if (ifr->ifr_hwaddr.sa_family != dev->type)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			err = dev->set_mac_address(dev, &ifr->ifr_hwaddr);
+			if (!err)
+				notifier_call_chain(&netdev_chain,
+						    NETDEV_CHANGEADDR, dev);
+			return err;
+
+		case SIOCSIFHWBROADCAST:
+			if (ifr->ifr_hwaddr.sa_family != dev->type)
+				return -EINVAL;
+			memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
+			       MAX_ADDR_LEN);
+			notifier_call_chain(&netdev_chain,
+					    NETDEV_CHANGEADDR, dev);
+			return 0;
+
+		case SIOCGIFMAP:
+			ifr->ifr_map.mem_start = dev->mem_start;
+			ifr->ifr_map.mem_end   = dev->mem_end;
+			ifr->ifr_map.base_addr = dev->base_addr;
+			ifr->ifr_map.irq       = dev->irq;
+			ifr->ifr_map.dma       = dev->dma;
+			ifr->ifr_map.port      = dev->if_port;
+			return 0;
+
+		case SIOCSIFMAP:
+			if (dev->set_config) {
+				if (!netif_device_present(dev))
+					return -ENODEV;
+				return dev->set_config(dev, &ifr->ifr_map);
+			}
+			return -EOPNOTSUPP;
+
+		case SIOCADDMULTI:
+			if (!dev->set_multicast_list ||
+			    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
+				   dev->addr_len, 1);
+			return 0;
+
+		case SIOCDELMULTI:
+			if (!dev->set_multicast_list ||
+			    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
+				return -EINVAL;
+			if (!netif_device_present(dev))
+				return -ENODEV;
+			dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
+				      dev->addr_len, 1);
+			return 0;
+
+		case SIOCGIFINDEX:
+			ifr->ifr_ifindex = dev->ifindex;
+			return 0;
+
+		case SIOCGIFTXQLEN:
+			ifr->ifr_qlen = dev->tx_queue_len;
+			return 0;
+
+		case SIOCSIFTXQLEN:
+			if (ifr->ifr_qlen < 0)
+				return -EINVAL;
+			dev->tx_queue_len = ifr->ifr_qlen;
+			return 0;
+
+		case SIOCSIFNAME:
+			if (dev->flags & IFF_UP)
+				return -EBUSY;
+			if (__dev_get_by_name(ifr->ifr_newname))
+				return -EEXIST;
+			memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+			dev->name[IFNAMSIZ - 1] = 0;
+			notifier_call_chain(&netdev_chain,
+					    NETDEV_CHANGENAME, dev);
+			return 0;
+
+		/*
+		 *	Unknown or private ioctl
+		 */
+
+		default:
+			if ((cmd >= SIOCDEVPRIVATE &&
+			    cmd <= SIOCDEVPRIVATE + 15) ||
+			    cmd == SIOCBONDENSLAVE ||
+			    cmd == SIOCBONDRELEASE ||
+			    cmd == SIOCBONDSETHWADDR ||
+			    cmd == SIOCBONDSLAVEINFOQUERY ||
+			    cmd == SIOCBONDINFOQUERY ||
+			    cmd == SIOCBONDCHANGEACTIVE ||
+			    cmd == SIOCETHTOOL ||
+			    cmd == SIOCGMIIPHY ||
+			    cmd == SIOCGMIIREG ||
+			    cmd == SIOCSMIIREG ||
+			    cmd == SIOCWANDEV) {
+				err = -EOPNOTSUPP;
+				if (dev->do_ioctl) {
+					if (netif_device_present(dev))
+						err = dev->do_ioctl(dev, ifr,
+								    cmd);
+					else
+						err = -ENODEV;
+				}
+			} else
+				err = -EINVAL;
+
+	}
+	return err;
+}
+
+/*
+ *	This function handles all "interface"-type I/O control requests. The actual
+ *	'doing' part of this is dev_ifsioc above.
+ */
+
+/**
+ *	dev_ioctl	-	network device ioctl
+ *	@cmd: command to issue
+ *	@arg: pointer to a struct ifreq in user space
+ *
+ *	Issue ioctl functions to devices. This is normally called by the
+ *	user space syscall interfaces but can sometimes be useful for
+ *	other purposes. The return value is the return from the syscall if
+ *	positive or a negative errno code on error.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+	struct ifreq ifr;
+	int ret;
+	char *colon;
+
+	/* One special case: SIOCGIFCONF takes ifconf argument
+	   and requires shared lock, because it sleeps writing
+	   to user space.
+	 */
+
+	if (cmd == SIOCGIFCONF) {
+		rtnl_shlock();
+		ret = dev_ifconf((char *) arg);
+		rtnl_shunlock();
+		return ret;
+	}
+	if (cmd == SIOCGIFNAME)
+		return dev_ifname((struct ifreq *)arg);
+
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
+
+	ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+	colon = strchr(ifr.ifr_name, ':');
+	if (colon)
+		*colon = 0;
+
+	/*
+	 *	See which interface the caller is talking about.
+	 */
+
+	switch (cmd) {
+		/*
+		 *	These ioctl calls:
+		 *	- can be done by all.
+		 *	- atomic and do not require locking.
+		 *	- return a value
+		 */
+		case SIOCGIFFLAGS:
+		case SIOCGIFMETRIC:
+		case SIOCGIFMTU:
+		case SIOCGIFHWADDR:
+		case SIOCGIFSLAVE:
+		case SIOCGIFMAP:
+		case SIOCGIFINDEX:
+		case SIOCGIFTXQLEN:
+			dev_load(ifr.ifr_name);
+			read_lock(&dev_base_lock);
+			ret = dev_ifsioc(&ifr, cmd);
+			read_unlock(&dev_base_lock);
+			if (!ret) {
+				if (colon)
+					*colon = ':';
+				if (copy_to_user(arg, &ifr,
+						 sizeof(struct ifreq)))
+					ret = -EFAULT;
+			}
+			return ret;
+
+		/*
+		 *	These ioctl calls:
+		 *	- require superuser power.
+		 *	- require strict serialization.
+		 *	- return a value
+		 */
+		case SIOCETHTOOL:
+		case SIOCGMIIPHY:
+		case SIOCGMIIREG:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			dev_load(ifr.ifr_name);
+			dev_probe_lock();
+			rtnl_lock();
+			ret = dev_ifsioc(&ifr, cmd);
+			rtnl_unlock();
+			dev_probe_unlock();
+			if (!ret) {
+				if (colon)
+					*colon = ':';
+				if (copy_to_user(arg, &ifr,
+						 sizeof(struct ifreq)))
+					ret = -EFAULT;
+			}
+			return ret;
+
+		/*
+		 *	These ioctl calls:
+		 *	- require superuser power.
+		 *	- require strict serialization.
+		 *	- do not return a value
+		 */
+		case SIOCSIFFLAGS:
+		case SIOCSIFMETRIC:
+		case SIOCSIFMTU:
+		case SIOCSIFMAP:
+		case SIOCSIFHWADDR:
+		case SIOCSIFSLAVE:
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+		case SIOCSIFHWBROADCAST:
+		case SIOCSIFTXQLEN:
+		case SIOCSIFNAME:
+		case SIOCSMIIREG:
+		case SIOCBONDENSLAVE:
+		case SIOCBONDRELEASE:
+		case SIOCBONDSETHWADDR:
+		case SIOCBONDSLAVEINFOQUERY:
+		case SIOCBONDINFOQUERY:
+		case SIOCBONDCHANGEACTIVE:
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			dev_load(ifr.ifr_name);
+			dev_probe_lock();
+			rtnl_lock();
+			ret = dev_ifsioc(&ifr, cmd);
+			rtnl_unlock();
+			dev_probe_unlock();
+			return ret;
+
+		case SIOCGIFMEM:
+			/* Get the per device memory space. We can add this but
+			 * currently do not support it */
+		case SIOCSIFMEM:
+			/* Set the per device memory buffer space.
+			 * Not applicable in our case */
+		case SIOCSIFLINK:
+			return -EINVAL;
+
+		/*
+		 *	Unknown or private ioctl.
+		 */
+		default:
+			if (cmd == SIOCWANDEV ||
+			    (cmd >= SIOCDEVPRIVATE &&
+			     cmd <= SIOCDEVPRIVATE + 15)) {
+				dev_load(ifr.ifr_name);
+				dev_probe_lock();
+				rtnl_lock();
+				ret = dev_ifsioc(&ifr, cmd);
+				rtnl_unlock();
+				dev_probe_unlock();
+				if (!ret && copy_to_user(arg, &ifr,
+							 sizeof(struct ifreq)))
+					ret = -EFAULT;
+				return ret;
+			}
+#ifdef WIRELESS_EXT
+			/* Take care of Wireless Extensions */
+			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+				/* If command is `set a parameter', or
+				 * `get the encoding parameters', check if
+				 * the user has the right to do it */
+				if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE) {
+					if (!capable(CAP_NET_ADMIN))
+						return -EPERM;
+				}
+				dev_load(ifr.ifr_name);
+				rtnl_lock();
+				/* Follow me in net/core/wireless.c */
+				ret = wireless_process_ioctl(&ifr, cmd);
+				rtnl_unlock();
+				if (!ret && IW_IS_GET(cmd) &&
+				    copy_to_user(arg, &ifr,
+					    	 sizeof(struct ifreq)))
+					ret = -EFAULT;
+				return ret;
+			}
+#endif	/* WIRELESS_EXT */
+			return -EINVAL;
+	}
+}
+
+
+/**
+ *	dev_new_index	-	allocate an ifindex
+ *
+ *	Returns a suitable unique value for a new device interface
+ *	number.  The caller must hold the rtnl semaphore or the
+ *	dev_base_lock to be sure it remains unique.
+ */
+int dev_new_index(void)
+{
+	static int ifindex;
+	for (;;) {
+		if (++ifindex <= 0)
+			ifindex = 1;
+		if (!__dev_get_by_index(ifindex))
+			return ifindex;
+	}
+}
+
+static int dev_boot_phase = 1;
+
+/**
+ *	register_netdevice	- register a network device
+ *	@dev: device to register
+ *
+ *	Take a completed network device structure and add it to the kernel
+ *	interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
+ *	chain. 0 is returned on success. A negative errno code is returned
+ *	on a failure to set up the device, or if the name is a duplicate.
+ *
+ *	Callers must hold the rtnl semaphore.  See the comment at the
+ *	end of Space.c for details about the locking.  You may want
+ *	register_netdev() instead of this.
+ *
+ *	BUGS:
+ *	The locking appears insufficient to guarantee two parallel registers
+ *	will not get the same name.
+ */
+
+int register_netdevice(struct net_device *dev)
+{
+	struct net_device *d, **dp;
+	int ret;
+
+	BUG_ON(dev_boot_phase);
+
+	spin_lock_init(&dev->queue_lock);
+	spin_lock_init(&dev->xmit_lock);
+	dev->xmit_lock_owner = -1;
+#ifdef CONFIG_NET_FASTROUTE
+	dev->fastpath_lock = RW_LOCK_UNLOCKED;
+#endif
+
+#ifdef CONFIG_NET_DIVERT
+	ret = alloc_divert_blk(dev);
+	if (ret)
+		goto out;
+#endif /* CONFIG_NET_DIVERT */
+
+	dev->iflink = -1;
+
+	/* Init, if this function is available */
+	ret = -EIO;
+	if (dev->init && dev->init(dev))
+		goto out_err;
+
+	dev->ifindex = dev_new_index();
+	if (dev->iflink == -1)
+		dev->iflink = dev->ifindex;
+
+	/* Check for existence, and append to tail of chain */
+	ret = -EEXIST;
+	for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {
+		if (d == dev || !strcmp(d->name, dev->name))
+			goto out_err;
+	}
+	/*
+	 *	nil rebuild_header routine,
+	 *	that should be never called and used as just bug trap.
+	 */
+
+	if (!dev->rebuild_header)
+		dev->rebuild_header = default_rebuild_header;
+
+	/*
+	 *	Default initial state at registry is that the
+	 *	device is present.
+	 */
+
+	set_bit(__LINK_STATE_PRESENT, &dev->state);
+
+	dev->next = NULL;
+	dev_init_scheduler(dev);
+	write_lock_bh(&dev_base_lock);
+	*dp = dev;
+	dev_hold(dev);
+	dev->deadbeaf = 0;
+	write_unlock_bh(&dev_base_lock);
+
+	/* Notify protocols, that a new device appeared. */
+	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
+
+	net_run_sbin_hotplug(dev, "register");
+	ret = 0;
+
+out:
+	return ret;
+out_err:
+#ifdef CONFIG_NET_DIVERT
+	free_divert_blk(dev);
+#endif
+	goto out;
+}
+
+/**
+ *	netdev_finish_unregister - complete unregistration
+ *	@dev: device
+ *
+ *	Destroy and free a dead device. A value of zero is returned on
+ *	success.
+ */
+int netdev_finish_unregister(struct net_device *dev)
+{
+	BUG_TRAP(!dev->ip_ptr);
+	BUG_TRAP(!dev->ip6_ptr);
+	BUG_TRAP(!dev->dn_ptr);
+
+	if (!dev->deadbeaf) {
+		printk(KERN_ERR "Freeing alive device %p, %s\n",
+		       dev, dev->name);
+		return 0;
+	}
+#ifdef NET_REFCNT_DEBUG
+	printk(KERN_DEBUG "netdev_finish_unregister: %s%s.\n", dev->name,
+	       (dev->features & NETIF_F_DYNALLOC)?"":", old style");
+#endif
+	if (dev->destructor)
+		dev->destructor(dev);
+	if (dev->features & NETIF_F_DYNALLOC)
+		kfree(dev);
+	return 0;
+}
+
+/**
+ *	unregister_netdevice - remove device from the kernel
+ *	@dev: device
+ *
+ *	This function shuts down a device interface and removes it
+ *	from the kernel tables. On success 0 is returned, on a failure
+ *	a negative errno code is returned.
+ *
+ *	Callers must hold the rtnl semaphore.  See the comment at the
+ *	end of Space.c for details about the locking.  You may want
+ *	unregister_netdev() instead of this.
+ */
+
+int unregister_netdevice(struct net_device *dev)
+{
+	unsigned long now, warning_time;
+	struct net_device *d, **dp;
+
+	BUG_ON(dev_boot_phase);
+
+	/* If device is running, close it first. */
+	if (dev->flags & IFF_UP)
+		dev_close(dev);
+
+	BUG_TRAP(!dev->deadbeaf);
+	dev->deadbeaf = 1;
+
+	/* And unlink it from device chain. */
+	for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {
+		if (d == dev) {
+			write_lock_bh(&dev_base_lock);
+			*dp = d->next;
+			write_unlock_bh(&dev_base_lock);
+			break;
+		}
+	}
+	if (!d) {
+		printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
+				  "was registered\n", dev->name, dev);
+		return -ENODEV;
+	}
+
+	/* Synchronize to net_rx_action. */
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+
+#ifdef CONFIG_NET_FASTROUTE
+	dev_clear_fastroute(dev);
+#endif
+
+	/* Shutdown queueing discipline. */
+	dev_shutdown(dev);
+	
+	net_run_sbin_hotplug(dev, "unregister");
+	
+	/* Notify protocols, that we are about to destroy
+	   this device. They should clean all the things.
+	*/
+	notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
+	
+	/*
+	 *	Flush the multicast chain
+	 */
+	dev_mc_discard(dev);
+
+	if (dev->uninit)
+		dev->uninit(dev);
+
+	/* Notifier chain MUST detach us from master device. */
+	BUG_TRAP(!dev->master);
+
+#ifdef CONFIG_NET_DIVERT
+	free_divert_blk(dev);
+#endif
+
+	if (dev->features & NETIF_F_DYNALLOC) {
+#ifdef NET_REFCNT_DEBUG
+		if (atomic_read(&dev->refcnt) != 1)
+			printk(KERN_DEBUG "unregister_netdevice: holding %s "
+					  "refcnt=%d\n",
+			       dev->name, atomic_read(&dev->refcnt) - 1);
+#endif
+		goto out;
+	}
+
+	/* Last reference is our one */
+	if (atomic_read(&dev->refcnt) == 1)
+		goto out;
+
+#ifdef NET_REFCNT_DEBUG
+	printk(KERN_DEBUG "unregister_netdevice: waiting %s refcnt=%d\n",
+	       dev->name, atomic_read(&dev->refcnt));
+#endif
+
+	/* EXPLANATION. If dev->refcnt is not now 1 (our own reference)
+	   it means that someone in the kernel still has a reference
+	   to this device and we cannot release it.
+
+	   "New style" devices have destructors, hence we can return from this
+	   function and destructor will do all the work later. As of kernel
+	   2.4.0 there are very few "New Style" devices.
+
+	   "Old style" devices expect that the device is free of any references
+	   upon exit from this function.
+	   We cannot return from this function until all such references have
+	   fallen away. This is because the caller of this function will
+	   probably immediately kfree(*dev) and then be unloaded via
+	   sys_delete_module.
+
+	   So, we linger until all references fall away.  The duration of the
+	   linger is basically unbounded! It is driven by, for example, the
+	   current setting of sysctl_ipfrag_time.
+
+	   After 1 second, we start to rebroadcast unregister notifications
+	   in hope that careless clients will release the device.
+
+	 */
+
+	now = warning_time = jiffies;
+	while (atomic_read(&dev->refcnt) != 1) {
+		if ((jiffies - now) > 1 * HZ) {
+			/* Rebroadcast unregister notification */
+			notifier_call_chain(&netdev_chain,
+					    NETDEV_UNREGISTER, dev);
+		}
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(HZ / 4);
+		current->state = TASK_RUNNING;
+		if ((jiffies - warning_time) > 10 * HZ) {
+			printk(KERN_EMERG "unregister_netdevice: waiting for "
+			       "%s to become free. Usage count = %d\n",
+			       dev->name, atomic_read(&dev->refcnt));
+			warning_time = jiffies;
+		}
+	}
+out:
+	dev_put(dev);
+	return 0;
+}
+
+
+/*
+ *	Initialize the DEV module. At boot time this walks the device list and
+ *	unhooks any devices that fail to initialise (normally hardware not
+ *	present) and leaves us with a valid list of present and active devices.
+ *
+ */
+
+extern void net_device_init(void);
+extern void ip_auto_config(void);
+#ifdef CONFIG_NET_DIVERT
+extern void dv_init(void);
+#endif /* CONFIG_NET_DIVERT */
+
+
+/*
+ *       This is called single threaded during boot, so no need
+ *       to take the rtnl semaphore.
+ */
+static int __init net_dev_init(void)
+{
+	struct net_device *dev, **dp;
+	int i;
+
+	BUG_ON(!dev_boot_phase);
+
+#ifdef CONFIG_NET_DIVERT
+	dv_init();
+#endif /* CONFIG_NET_DIVERT */
+
+	/*
+	 *	Initialise the packet receive queues.
+	 */
+
+	for (i = 0; i < NR_CPUS; i++) {
+		struct softnet_data *queue;
+
+		queue = &softnet_data[i];
+		skb_queue_head_init(&queue->input_pkt_queue);
+		queue->throttle = 0;
+		queue->cng_level = 0;
+		queue->avg_blog = 10; /* arbitrary non-zero */
+		queue->completion_queue = NULL;
+		INIT_LIST_HEAD(&queue->poll_list);
+		set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
+		queue->backlog_dev.weight = weight_p;
+		queue->backlog_dev.poll = process_backlog;
+		atomic_set(&queue->backlog_dev.refcnt, 1);
+	}
+
+#ifdef CONFIG_NET_PROFILE
+	net_profile_init();
+	NET_PROFILE_REGISTER(dev_queue_xmit);
+	NET_PROFILE_REGISTER(softnet_process);
+#endif
+
+#ifdef OFFLINE_SAMPLE
+	samp_timer.expires = jiffies + (10 * HZ);
+	add_timer(&samp_timer);
+#endif
+
+	/*
+	 *	Add the devices.
+	 *	If the call to dev->init fails, the dev is removed
+	 *	from the chain disconnecting the device until the
+	 *	next reboot.
+	 *
+	 *	NB At boot phase networking is dead. No locking is required.
+	 *	But we still preserve dev_base_lock for sanity.
+	 */
+
+	dp = &dev_base;
+	while ((dev = *dp) != NULL) {
+		spin_lock_init(&dev->queue_lock);
+		spin_lock_init(&dev->xmit_lock);
+#ifdef CONFIG_NET_FASTROUTE
+		dev->fastpath_lock = RW_LOCK_UNLOCKED;
+#endif
+		dev->xmit_lock_owner = -1;
+		dev->iflink = -1;
+		dev_hold(dev);
+
+		/*
+		 * Allocate name. If the init() fails
+		 * the name will be reissued correctly.
+		 */
+		if (strchr(dev->name, '%'))
+			dev_alloc_name(dev, dev->name);
+
+		/*
+		 * Check boot time settings for the device.
+		 */
+		netdev_boot_setup_check(dev);
+
+		if (dev->init && dev->init(dev)) {
+			/*
+			 * It failed to come up. It will be unhooked later.
+			 * dev_alloc_name can now advance to next suitable
+			 * name that is checked next.
+			 */
+			dev->deadbeaf = 1;
+			dp = &dev->next;
+		} else {
+			dp = &dev->next;
+			dev->ifindex = dev_new_index();
+			if (dev->iflink == -1)
+				dev->iflink = dev->ifindex;
+			if (!dev->rebuild_header)
+				dev->rebuild_header = default_rebuild_header;
+			dev_init_scheduler(dev);
+			set_bit(__LINK_STATE_PRESENT, &dev->state);
+		}
+	}
+
+	/*
+	 * Unhook devices that failed to come up
+	 */
+	dp = &dev_base;
+	while ((dev = *dp) != NULL) {
+		if (dev->deadbeaf) {
+			write_lock_bh(&dev_base_lock);
+			*dp = dev->next;
+			write_unlock_bh(&dev_base_lock);
+			dev_put(dev);
+		} else {
+			dp = &dev->next;
+		}
+	}
+
+#ifdef CONFIG_PROC_FS
+	proc_net_create("dev", 0, dev_get_info);
+	create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL);
+#ifdef WIRELESS_EXT
+	/* Available in net/core/wireless.c */
+	proc_net_create("wireless", 0, dev_get_wireless_info);
+#endif	/* WIRELESS_EXT */
+#endif	/* CONFIG_PROC_FS */
+
+	dev_boot_phase = 0;
+
+	open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
+	open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
+
+	dst_init();
+	dev_mcast_init();
+
+#ifdef CONFIG_NET_SCHED
+	pktsched_init();
+#endif
+	/*
+	 *	Initialise network devices
+	 */
+
+	net_device_init();
+
+	return 0;
+}
+
+subsys_initcall(net_dev_init);
+
+#ifdef CONFIG_HOTPLUG
+
+/* Notify userspace when a netdevice event occurs,
+ * by running '/sbin/hotplug net' with certain
+ * environment variables set.
+ */
+
+static int net_run_sbin_hotplug(struct net_device *dev, char *action)
+{
+	char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32];
+	int i;
+
+	sprintf(ifname, "INTERFACE=%s", dev->name);
+	sprintf(action_str, "ACTION=%s", action);
+
+        i = 0;
+        argv[i++] = hotplug_path;
+        argv[i++] = "net";
+        argv[i] = 0;
+
+	i = 0;
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp [i++] = ifname;
+	envp [i++] = action_str;
+	envp [i] = 0;
+
+	return call_usermodehelper(argv [0], argv, envp);
+}
+#endif
diff --git a/kernel/linux2.5/net/ebt_README b/kernel/linux2.5/net/ebt_README
new file mode 100644
index 0000000..7a446d4
--- /dev/null
+++ b/kernel/linux2.5/net/ebt_README
@@ -0,0 +1,2 @@
+The net/Makefile is actually unchanged w.r.t. 2.5.x, but 2.4.x changes the
+net/Makefile so we must have the 2.5.x here.
diff --git a/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff
new file mode 100644
index 0000000..269d739
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff
@@ -0,0 +1,3998 @@
+ebtables-v2.0-rc1 - 31 July
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/br_private.h	Wed Jul 31 20:42:36 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0-rc1/include/linux/if_bridge.h	Wed Jul 31 20:42:36 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0-rc1/net/core/dev.c	Wed Jul 31 20:42:44 2002
+@@ -1384,7 +1384,7 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1394,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1479,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/br_input.c	Wed Jul 31 20:42:36 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/bridge/br.c	Wed Jul 31 20:44:49 2002
++++ ebt2.0-rc1/net/bridge/br.c	Wed Jul 31 20:42:44 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++                        const struct net_device *in,
++                        const struct net_device *out,
++                        int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -82,7 +90,12 @@
+ #endif
+ }
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#else
+ EXPORT_NO_SYMBOLS;
++#endif
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux/net/bridge/Makefile	Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/Makefile	Wed Jul 31 20:42:44 2002
+@@ -7,6 +7,12 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0-rc1/include/linux/netfilter_bridge.h	Wed Jul 31 20:42:44 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0-rc1/net/Makefile	Wed Jul 31 20:42:44 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/Config.in	Wed Jul 31 20:42:36 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/Makefile	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,28 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/Config.in	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,18 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/br_db.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_filter.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,92 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_nat.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_broute.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_mark_t
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++// The target member of the struct ebt_vlan_info provides the same
++// functionality as a separate table
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++	if ((*pskb)->nfmark != infostuff->mark) {
++		(*pskb)->nfmark = infostuff->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return infostuff->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark_m.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,62 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_mark_m_info)) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_redirect.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,74 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_arp.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_ip.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_vlan.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (infostuff->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   infostuff->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   infostuff->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!infostuff->id) {	/* if id!=0 => check vid range */
++			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     infostuff->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) infostuff->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     infostuff->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (infostuff->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_log.c	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_snat.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,68 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_dnat.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,68 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtables.c	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,1503 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strncmp(entry, device->name, IFNAMSIZ);
++}
++
++#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (p->bitmask & EBT_802_3) {
++		if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(p->bitmask & EBT_NOPROTO) &&
++	   FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++		return 1;
++	if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
++	   p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
++	   (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++	
++	if (p->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
++			   p->sourcemsk[i];
++		if (FWINV(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (p->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ p->destmac[i]) &
++			   p->destmsk[i];
++		if (FWINV(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries, \
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++		   out, counter_base + i) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out, counter_base + i);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt += counter_base[i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebtables.h	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,347 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_arp.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_ip.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_vlan.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u8 prio;		/* VLAN User Priority {0-7} */
++	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_log.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_nat.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_redirect.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_m.h	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	__u8 invert;
++	__u8 bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_t.h	Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/br_db.h	Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0-rc2-dev_vs_2.5.32.diff b/kernel/patches/base-patches/ebtables-v2.0-rc2-dev_vs_2.5.32.diff
new file mode 100644
index 0000000..df064ed
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0-rc2-dev_vs_2.5.32.diff
@@ -0,0 +1,3590 @@
+ebtables-v2.0-rc2-dev vs 2.5.32 - 29 August 2002
+
+*** modifications for brouter support ***
+
+--- linux-2.5.32/net/bridge/br_private.h	Tue Aug 27 21:26:37 2002
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/br_private.h	Thu Aug 29 21:41:11 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.5.32/include/linux/if_bridge.h	Tue Aug 27 21:26:43 2002
++++ linux-2.5.32-ebt2.0-rc2/include/linux/if_bridge.h	Thu Aug 29 21:41:11 2002
+@@ -102,8 +102,13 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux-2.5.32/net/core/dev.c	Tue Aug 27 21:26:43 2002
++++ linux-2.5.32-ebt2.0-rc2/net/core/dev.c	Thu Aug 29 21:41:11 2002
+@@ -1394,7 +1394,7 @@ void net_call_rx_atomic(void (*fn)(void)
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1411,7 +1411,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1472,7 +1471,11 @@ int netif_receive_skb(struct sk_buff *sk
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port && br_handle_frame_hook) {
+-		return handle_bridge(skb, pt_prev);
++                int ret;
++
++                ret = handle_bridge(skb, pt_prev);
++                if (br_handle_frame_hook(skb) == 0)
++                        return ret;
+ 	}
+ #endif
+ 
+--- linux-2.5.32/net/bridge/br_input.c	Tue Aug 27 21:26:37 2002
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/br_input.c	Thu Aug 29 21:41:11 2002
+@@ -19,6 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ 
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+@@ -112,7 +116,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +150,32 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
+ 	read_unlock(&br->lock);
++	return 0;
+ }
+--- linux-2.5.32/net/bridge/br.c	Tue Aug 27 21:26:35 2002
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/br.c	Thu Aug 29 21:41:11 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++                        const struct net_device *in,
++                        const struct net_device *out,
++                        int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -73,6 +81,11 @@ static void __exit br_deinit(void)
+ 	br_fdb_put_hook = NULL;
+ #endif
+ }
++
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.5.32/net/bridge/Makefile	Tue Aug 27 21:27:32 2002
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/Makefile	Thu Aug 29 21:41:11 2002
+@@ -2,10 +2,17 @@
+ # Makefile for the IEEE 802.1d ethernet bridging layer.
+ #
+ 
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ obj-$(CONFIG_BRIDGE) += bridge.o
+ 
+ bridge-objs	:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++obj-$(CONFIG_BRIDGE_EBT) += netfilter/
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.5.32/include/linux/netfilter_bridge.h	Tue Aug 27 21:26:45 2002
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge.h	Thu Aug 29 21:41:11 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux-2.5.32/net/Config.in	Tue Aug 27 21:26:37 2002
++++ linux-2.5.32-ebt2.0-rc2/net/Config.in	Thu Aug 29 21:48:30 2002
+@@ -62,6 +62,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/Makefile	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,20 @@
++#
++# Makefile for the netfilter modules for Link Layer filtering on a bridge.
++#
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/Config.in	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,17 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/Config.help	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,105 @@
++CONFIG_BRIDGE_EBT
++  ebtables is an extendable frame filtering system for the Linux
++  Ethernet bridge. Its usage and implementation is very similar to that
++  of iptables.
++  The difference is that ebtables works on the Link Layer, while iptables
++  works on the Network Layer. ebtables can filter all frames that come
++  into contact with a logical bridge device.
++  Apart from filtering, ebtables also allows MAC source and destination
++  alterations (we call it MAC SNAT and MAC DNAT) and also provides
++  functionality for making Linux a brouter.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_T_FILTER
++  The ebtables filter table is used to define frame filtering rules at
++  local input, forwarding and local output. See the man page for
++  ebtables(8).
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_T_NAT
++  The ebtables nat table is used to define rules that alter the MAC
++  source address (MAC SNAT) or the MAC destination address (MAC DNAT).
++  See the man page for ebtables(8).
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_BROUTE
++  The ebtables broute table is used to define rules that decide between
++  bridging and routing frames, giving Linux the functionality of a
++  brouter. See the man page for ebtables(8) and examples on the ebtables
++  website.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_LOG
++  This option adds the log target, that you can use in any rule in
++  any ebtables table. It records the frame header to the syslog.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_IPF
++  This option adds the IP match, which allows basic IP header field
++  filtering.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_ARPF
++  This option adds the ARP match, which allows ARP and RARP header field
++  filtering.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_VLANF
++  This option adds the 802.1Q vlan match, which allows the filtering of
++  802.1Q vlan fields.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_MARKF
++  This option adds the mark match, which allows matching frames based on
++  the 'nfmark' value in the frame. This can be set by the mark target.
++  This value is the same as the one used in the iptables mark match and
++  target.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_SNAT
++  This option adds the MAC SNAT target, which allows altering the MAC
++  source address of frames.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_DNAT
++  This option adds the MAC DNAT target, which allows altering the MAC
++  destination address of frames.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_REDIRECT
++  This option adds the MAC redirect target, which allows altering the MAC
++  destination address of a frame to that of the device it arrived on.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
++
++CONFIG_BRIDGE_EBT_MARK_T
++  This option adds the mark target, which allows marking frames by
++  setting the 'nfmark' value in the frame.
++  This value is the same as the one used in the iptables mark match and
++  target.
++
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  If unsure, say `N'.
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebtable_filter.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebtable_nat.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebtable_broute.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,75 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_mark.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_mark_m.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_redirect.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_arp.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_ip.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_vlan.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (info->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) info->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     info->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (info->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_log.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_snat.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebt_dnat.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/net/bridge/netfilter/ebtables.c	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,1484 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   smp_processor_id());
++	if (private->chainstack)
++		cs = private->chainstack[smp_processor_id()];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(NR_CPUS * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < NR_CPUS; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < NR_CPUS; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt += counter_base[i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * NR_CPUS;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < NR_CPUS; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < NR_CPUS; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * NR_CPUS;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < NR_CPUS; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < NR_CPUS; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebtables.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,358 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_arp.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_ip.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_vlan.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_log.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_nat.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_redirect.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_mark_m.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.5.32-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_mark_t.h	Thu Aug 29 21:41:11 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0-rc2_vs_2.4.20-pre5.diff b/kernel/patches/base-patches/ebtables-v2.0-rc2_vs_2.4.20-pre5.diff
new file mode 100644
index 0000000..531ddcf
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0-rc2_vs_2.4.20-pre5.diff
@@ -0,0 +1,3510 @@
+ebtables-v2.0-rc2 vs 2.4.20-pre5 - 30 August 2002
+
+*** modifications for brouter support ***
+
+--- linux-2.4.20-pre5/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br_private.h	Fri Aug 30 20:04:35 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.4.20-pre5/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/if_bridge.h	Fri Aug 30 20:04:35 2002
+@@ -102,8 +102,13 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux-2.4.20-pre5/net/core/dev.c	Fri Aug 30 19:48:58 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/core/dev.c	Fri Aug 30 20:04:35 2002
+@@ -1392,7 +1392,7 @@ void net_call_rx_atomic(void (*fn)(void)
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1409,7 +1409,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1469,7 +1468,11 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
+ 	}
+ #endif
+ 
+--- linux-2.4.20-pre5/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br_input.c	Fri Aug 30 20:04:35 2002
+@@ -19,6 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ 
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+@@ -112,7 +116,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +150,32 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.20-pre5/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/br.c	Fri Aug 30 20:04:35 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++                        const struct net_device *in,
++                        const struct net_device *out,
++                        int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -74,7 +82,12 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#else
+ EXPORT_NO_SYMBOLS;
++#endif
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20-pre5/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/Makefile	Fri Aug 30 20:04:35 2002
+@@ -7,6 +7,12 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux-2.4.20-pre5/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge.h	Fri Aug 30 20:04:35 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux-2.4.20-pre5/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/Makefile	Fri Aug 30 20:04:35 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@ subdir-$(CONFIG_IPV6)		+= ipv6
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux-2.4.20-pre5/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/Config.in	Fri Aug 30 20:04:35 2002
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/Makefile	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,27 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/Config.in	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,17 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebtable_filter.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebtable_nat.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebtable_broute.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,75 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_mark.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_mark_m.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_redirect.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_arp.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_ip.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_vlan.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (info->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) info->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     info->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (info->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_log.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_snat.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebt_dnat.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/net/bridge/netfilter/ebtables.c	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,1484 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt += counter_base[i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebtables.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,358 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_arp.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_ip.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_vlan.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_log.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_nat.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_redirect.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_mark_m.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebt2.0-rc2/include/linux/netfilter_bridge/ebt_mark_t.h	Fri Aug 30 20:04:35 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0_vs_2.4.20-pre5.diff b/kernel/patches/base-patches/ebtables-v2.0_vs_2.4.20-pre5.diff
new file mode 100644
index 0000000..5c255c7
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0_vs_2.4.20-pre5.diff
@@ -0,0 +1,3494 @@
+ebtables-v2.0 vs 2.4.20-pre5 - 17 September 2002
+
+--- linux-2.4.20-pre5/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br_private.h	Wed Sep 18 20:51:58 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.4.20-pre5/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.20-pre5-ebtables/include/linux/if_bridge.h	Wed Sep 18 20:51:58 2002
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.20-pre5/net/core/dev.c	Wed Sep 18 20:50:56 2002
++++ linux-2.4.20-pre5-ebtables/net/core/dev.c	Wed Sep 18 20:51:58 2002
+@@ -1392,7 +1392,7 @@ void net_call_rx_atomic(void (*fn)(void)
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1409,7 +1409,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1469,7 +1468,12 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+--- linux-2.4.20-pre5/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br_input.c	Wed Sep 18 20:51:58 2002
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,29 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb))
++			return -1;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.20-pre5/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br_forward.c	Wed Sep 18 20:51:58 2002
+@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ 			__br_forward_finish);
+ }
+--- linux-2.4.20-pre5/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br.c	Wed Sep 18 20:51:58 2002
+@@ -28,6 +28,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -74,7 +76,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20-pre5/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/Makefile	Wed Sep 18 20:51:58 2002
+@@ -7,6 +7,8 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux-2.4.20-pre5/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge.h	Wed Sep 18 20:51:58 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+--- linux-2.4.20-pre5/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebtables/net/Makefile	Wed Sep 18 20:51:58 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@ subdir-$(CONFIG_IPV6)		+= ipv6
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux-2.4.20-pre5/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebtables/net/Config.in	Wed Sep 18 20:51:58 2002
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/Makefile	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,27 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/Config.in	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebtable_filter.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebtable_nat.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebtable_broute.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_mark.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_mark_m.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_redirect.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_arp.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_ip.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_vlan.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (info->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) info->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     info->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (info->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_log.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_snat.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_dnat.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebtables.c	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,1484 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt += counter_base[i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebtables.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,358 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_arp.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_ip.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_vlan.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_log.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_nat.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_redirect.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_mark_m.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebt_mark_t.h	Wed Sep 18 20:51:58 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre10_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre10_vs_2.4.18.diff
new file mode 100644
index 0000000..3bc0e24
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre10_vs_2.4.18.diff
@@ -0,0 +1,3782 @@
+ebtables-v2.0pre10 - 06 July
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Sat Jul  6 11:20:57 2002
++++ ebt2.0pre10/net/bridge/br_private.h	Sat Jul  6 10:52:44 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre10/include/linux/if_bridge.h	Sat Jul  6 10:52:42 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre10/net/core/dev.c	Sat Jul  6 10:52:44 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Sat Jul  6 11:20:57 2002
++++ ebt2.0pre10/net/bridge/br_input.c	Sat Jul  6 10:52:44 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre10/net/netsyms.c	Sat Jul  6 10:52:44 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre10/include/linux/netfilter_bridge.h	Sat Jul  6 10:52:43 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre10/net/Makefile	Sat Jul  6 10:52:44 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Sat Jul  6 11:20:57 2002
++++ ebt2.0pre10/net/Config.in	Sat Jul  6 10:52:44 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/Makefile	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,26 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/Config.in	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/br_db.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebtable_filter.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,92 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebtable_nat.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,155 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebtable_broute.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTE", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_redirect.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_arp.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_ip.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_vlan.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (infostuff->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   infostuff->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   infostuff->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!infostuff->id) {	/* if id!=0 => check vid range */
++			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     infostuff->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) infostuff->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     infostuff->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (infostuff->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_log.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_snat.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebt_dnat.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/net/bridge/netfilter/ebtables.c	Sat Jul  6 10:52:44 2002
+@@ -0,0 +1,1453 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, j, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++
++	read_lock_bh(&table->lock);
++	cs = table->private->chainstack;
++	chaininfo = table->private->hook_entry[hook];
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	#define cb_base table->private->counters + \
++	   cpu_number_map(smp_processor_id()) * table->private->nentries
++	counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++ 	while (i < nentries) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			if (point->bitmask & EBT_SOURCEMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_source[j] ^ point->sourcemac[j]) &
++					   point->sourcemsk[j];
++				if (FWINV(!!verdict, EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_dest[j] ^ point->destmac[j]) &
++					   point->destmsk[j];
++				if (FWINV(!!verdict, EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict == EBT_RETURN) {
++letsreturn:
++				if (sp == 0)
++					// act like this is EBT_CONTINUE
++					goto letscontinue;
++				sp--;
++				// put all the local variables right
++				i = cs[sp].n;
++				chaininfo = cs[sp].chaininfo;
++				nentries = chaininfo->nentries;
++				point = cs[sp].e;
++				counter_base = cb_base +
++				   chaininfo->counter_offset;
++				continue;
++			}
++			if (verdict == EBT_CONTINUE)
++				goto letscontinue;
++			if (verdict < 0) {
++				BUGPRINT("bogus standard verdict\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			// jump to a udc
++			cs[sp].n = i + 1;
++			cs[sp].chaininfo = chaininfo;
++			cs[sp].e = (struct ebt_entry *)
++			   (((char *)point) + point->next_offset);
++			i = 0;
++			chaininfo = (struct ebt_entries *) (base + verdict);
++			if (chaininfo->distinguisher) {
++				BUGPRINT("jump to non-chain\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			nentries = chaininfo->nentries;
++			point = (struct ebt_entry *)chaininfo->data;
++			counter_base = cb_base + chaininfo->counter_offset;
++			sp++;
++			continue;
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match) 
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher) 
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_unlock;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	if (table->chainstack)
++		vfree(table->chainstack);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack)
++		vfree(table->private->chainstack);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebtables.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,345 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack *chainstack;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_arp.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_ip.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_vlan.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u8 prio;		/* VLAN User Priority {0-7} */
++	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_log.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_nat.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/netfilter_bridge/ebt_redirect.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre10/include/linux/br_db.h	Sat Jul  6 10:52:43 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre11_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre11_vs_2.4.18.diff
new file mode 100644
index 0000000..581a75a
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre11_vs_2.4.18.diff
@@ -0,0 +1,3834 @@
+ebtables-v2.0pre11 - 16 July
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Tue Jul 16 18:58:28 2002
++++ ebt2.0pre11/net/bridge/br_private.h	Sat Jul  6 12:12:07 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre11/include/linux/if_bridge.h	Sat Jul  6 11:58:49 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre11/net/core/dev.c	Sat Jul  6 11:22:24 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Tue Jul 16 18:58:28 2002
++++ ebt2.0pre11/net/bridge/br_input.c	Sat Jul  6 11:22:24 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre11/net/netsyms.c	Sat Jul  6 11:22:24 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre11/include/linux/netfilter_bridge.h	Sat Jul  6 12:12:22 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre11/net/Makefile	Sat Jul  6 11:22:24 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Tue Jul 16 18:58:28 2002
++++ ebt2.0pre11/net/Config.in	Sat Jul  6 11:22:24 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/Makefile	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,26 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/Config.in	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/br_db.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebtable_filter.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,92 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebtable_nat.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,155 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebtable_broute.c	Tue Jul  9 20:04:31 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_redirect.c	Tue Jul  9 15:41:46 2002
+@@ -0,0 +1,70 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_arp.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_ip.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_vlan.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (infostuff->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   infostuff->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   infostuff->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!infostuff->id) {	/* if id!=0 => check vid range */
++			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     infostuff->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) infostuff->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     infostuff->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (infostuff->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_log.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_snat.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebt_dnat.c	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/net/bridge/netfilter/ebtables.c	Sun Jul 14 20:13:16 2002
+@@ -0,0 +1,1498 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, j, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	#define cb_base table->private->counters + \
++	   cpu_number_map(smp_processor_id()) * table->private->nentries
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++ 	while (i < nentries) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			if (point->bitmask & EBT_SOURCEMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_source[j] ^ point->sourcemac[j]) &
++					   point->sourcemsk[j];
++				if (FWINV(!!verdict, EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_dest[j] ^ point->destmac[j]) &
++					   point->destmsk[j];
++				if (FWINV(!!verdict, EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict == EBT_RETURN) {
++letsreturn:
++				if (sp == 0)
++					// act like this is EBT_CONTINUE
++					goto letscontinue;
++				sp--;
++				// put all the local variables right
++				i = cs[sp].n;
++				chaininfo = cs[sp].chaininfo;
++				nentries = chaininfo->nentries;
++				point = cs[sp].e;
++				counter_base = cb_base +
++				   chaininfo->counter_offset;
++				continue;
++			}
++			if (verdict == EBT_CONTINUE)
++				goto letscontinue;
++			if (verdict < 0) {
++				BUGPRINT("bogus standard verdict\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			// jump to a udc
++			cs[sp].n = i + 1;
++			cs[sp].chaininfo = chaininfo;
++			cs[sp].e = (struct ebt_entry *)
++			   (((char *)point) + point->next_offset);
++			i = 0;
++			chaininfo = (struct ebt_entries *) (base + verdict);
++			if (chaininfo->distinguisher) {
++				BUGPRINT("jump to non-chain\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			nentries = chaininfo->nentries;
++			point = (struct ebt_entry *)chaininfo->data;
++			counter_base = cb_base + chaininfo->counter_offset;
++			sp++;
++			continue;
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match) 
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher) 
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				break;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * nentries;
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt +=
++			   oldcounters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_unlock;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebtables.h	Sun Jul 14 19:19:09 2002
+@@ -0,0 +1,347 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_arp.h	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_ip.h	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_vlan.h	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u8 prio;		/* VLAN User Priority {0-7} */
++	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_log.h	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_nat.h	Tue Jul 16 18:45:16 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/netfilter_bridge/ebt_redirect.h	Sat Jul  6 11:22:24 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre11/include/linux/br_db.h	Sat Jul  6 12:13:39 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff
new file mode 100644
index 0000000..bab3e11
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff
@@ -0,0 +1,2621 @@
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre1/net/Makefile	Wed Apr  3 19:57:30 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Wed Apr  3 21:50:19 2002
++++ ebt2.0pre1/net/Config.in	Wed Apr  3 18:47:51 2002
+@@ -60,6 +60,7 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++   source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/Makefile	Wed Apr  3 18:48:52 2002
+@@ -0,0 +1,23 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/Config.in	Wed Apr  3 18:48:59 2002
+@@ -0,0 +1,12 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/br_db.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtable_filter.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,89 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200},
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200},
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtable_nat.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,149 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT
++// needed because of the bridge-nf patch (that allows use of iptables on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING to be executed _after_ the iptables stuff
++// when it's bridged, it's the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, 300},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, 200 + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_arp.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,95 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw)+sizeof(struct arphdr)+((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk), EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_ip.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,74 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS && FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE && FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	     FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_log.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,103 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++	      void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb,
++	      const struct net_device *in,
++	      const struct net_device *out, const void *data, unsigned int datalen,
++	      const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");// max length: 29 + 10 + 2 * 16
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));// length: 14
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));// max length: 46
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);// max length: 26
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++			ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ntohs(arph->ar_op));// max length: 40
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_nat.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,114 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (skb_cloned(*pskb)) {
++		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++		if (!nskb)
++			return EBT_DROP;
++		if ((*pskb)->sk)
++			skb_set_owner_w(nskb, (*pskb)->sk);
++		kfree_skb(*pskb);
++		*pskb = nskb;
++	}
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ETH_ALEN * sizeof(unsigned char));
++	return EBT_ACCEPT;
++}
++
++__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (skb_cloned(*pskb)) {
++		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++		if (!nskb)
++			return EBT_DROP;
++		if ((*pskb)->sk)
++			skb_set_owner_w(nskb, (*pskb)->sk);
++		kfree_skb(*pskb);
++		*pskb = nskb;
++	}
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ETH_ALEN * sizeof(unsigned char));
++	return EBT_ACCEPT;
++}
++
++int ebt_target_snat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	return 0;
++}
++
++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
++		return -EINVAL;
++	return 0;
++}
++
++struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, NULL, THIS_MODULE
++};
++
++struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtables.c	Wed Apr  3 18:48:47 2002
+@@ -0,0 +1,1088 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++/* used for print_string */
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h> /* list_named_find */
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data, w->watcher_size - sizeof(struct ebt_entry_watcher), c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data, m->match_size - sizeof(struct ebt_entry_match), c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++		  const struct net_device *in,
++		  const struct net_device *out,
++		  struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters + cpu_number_map(smp_processor_id()) * table->private->nentries + table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ){
++
++			if ( (point->bitmask & EBT_SOURCEMAC) &&
++			      FWINV(!!memcmp(point->sourcemac, ((**pskb).mac.ethernet)->h_source, ETH_ALEN), EBT_ISOURCE) )
++				goto letscontinue;
++
++			if ( (point->bitmask & EBT_DESTMAC) &&
++			      FWINV(!!memcmp(point->destmac, ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), EBT_IDEST) )
++				goto letscontinue;
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, out, counter_base + i);
++
++			t = (struct ebt_entry_target *) (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict = ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook, in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)(((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)list_named_find(&ebt_matches, m->u.name))) {
++		BUGPRINT("match does not exist: %s\n", m->u.name);
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data, m->match_size - sizeof(*m)) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)list_named_find(&ebt_watchers, w->u.name))) {
++		BUGPRINT("watcher does not exist: %s\n", w->u.name);
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data, w->watcher_size - sizeof(*w)) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e, struct ebt_table_info *newinfo,
++	char *base, char *limit, struct ebt_entries **hook_entries, unsigned int *n,
++	unsigned int *cnt, unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base == (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right, so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries) > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP && ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset || e->watchers_offset > e->target_offset || e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size - sizeof(*m));
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w));
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)list_named_find(&ebt_targets, t->u.name))) {
++		BUGPRINT("Target does not exist: %s\n", t->u.name);
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) > e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >= NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data, t->target_size - sizeof(*t)) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size - sizeof(*t));
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++	  struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	    repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the (last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL && (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	i = 0; // used to know what we need to clean up if something goes wrong
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called with under write_lock
++void get_counters(struct ebt_table_info *info, struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters, sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt += info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0, sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter allocation
++	// Only reason why I do this is because this way the lock is held only once,
++	// while this doesn't bring the kernel into a dangerous state.
++	if (tmp.num_counters &&
++		  copy_to_user(tmp.counters, counterstmp, tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size, ebt_cleanup_entry, NULL);
++	vfree(table->entries);
++
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries, table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++/* userspace just supplied us with counters */
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		BUGPRINT("Table not found for update_counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters, hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m, char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w, char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size + (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp, info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry, NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry, NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *) (((char *)(info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts
++= { { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebtables.h	Wed Apr  3 20:44:38 2002
+@@ -0,0 +1,304 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> /* IFNAMSIZ */
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> /* ETH_ALEN */
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++/* [gs]etsockopt numbers */
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses different
++// techniques for naming the policy and such. So, iptables doesn't need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	unsigned int match_size;// size of this struct + sizee of data
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	unsigned int watcher_size;// size of this struct + sizee of data
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	unsigned int target_size;// size of this struct + sizee of data
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target;
++	__u8 verdict;
++};
++
++/* one entry */
++struct ebt_entry {
++	__u32 bitmask; // this needs to be the first field
++	__u32 invflags;
++	__u16 ethproto;
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	unsigned int nentries; // nr of rules in the table
++	unsigned int entries_size; // total size of the entries
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; // start of the chains
++	unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it?
++	unsigned int num_counters; // nr of counters userspace expects back
++	struct ebt_counter *counters; // where the kernel will put the old counters
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *matchdata,
++	       unsigned int datalen, const struct ebt_counter *c);
++	// true == let it in
++	int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++	       void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *watcherdata,
++	       unsigned int datalen, const struct ebt_counter *c);
++	// true == let it in
++	int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++	       void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// true == let it in
++	int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++	       void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	unsigned int entries_size; // total size of the entries
++	unsigned int nentries;
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it?
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain matches, targets, ...
++	int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks);
++	struct ebt_table_info *private;// the data used by the kernel
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++	    const struct net_device *in,
++	    const struct net_device *out, struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++/* fn returns 0 to continue iteration */
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({                                        \
++	unsigned int __i;                       \
++	int __ret = 0;                          \
++	struct ebt_entry_match *__match;        \
++	                                        \
++	for (__i = sizeof(struct ebt_entry);    \
++	     __i < (e)->watchers_offset;        \
++	     __i += __match->match_size) {      \
++		__match = (void *)(e) + __i;          \
++		                                      \
++		__ret = fn(__match , ## args);        \
++		if (__ret != 0)                       \
++			break;                              \
++	}                                       \
++	if (__ret == 0) {                       \
++		if (__i != (e)->watchers_offset)      \
++			__ret = -EINVAL;                    \
++	}                                       \
++	__ret;                                  \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({                                          \
++	unsigned int __i;                         \
++	int __ret = 0;                            \
++	struct ebt_entry_watcher *__watcher;      \
++	                                          \
++	for (__i = e->watchers_offset;            \
++	     __i < (e)->target_offset;            \
++	     __i += __watcher->watcher_size) {    \
++		__watcher = (void *)(e) + __i;          \
++		                                        \
++		__ret = fn(__watcher , ## args);        \
++		if (__ret != 0)                         \
++			break;                                \
++	}                                         \
++	if (__ret == 0) {                         \
++		if (__i != (e)->target_offset)          \
++			__ret = -EINVAL;                      \
++	}                                         \
++	__ret;                                    \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({                                                    \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;                \
++		__ret = fn(__entry , ## args);                    \
++		if (__ret != 0)                                   \
++			break;                                          \
++		if (__entry->bitmask != 0)                        \
++		 __i += __entry->next_offset;                     \
++		else                                              \
++		 __i += sizeof(struct ebt_entries);               \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                                \
++			__ret = -EINVAL;                                \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_arp.h	Wed Apr  3 18:50:31 2002
+@@ -0,0 +1,25 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_ip.h	Wed Apr  3 18:50:31 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_log.h	Wed Apr  3 18:50:31 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_nat.h	Wed Apr  3 18:50:31 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/br_db.h	Wed Apr  3 20:45:01 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff
new file mode 100644
index 0000000..23ecce4
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff
@@ -0,0 +1,2752 @@
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre2/net/Makefile	Wed Apr  3 21:50:43 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Sun Apr 14 15:19:26 2002
++++ ebt2.0pre2/net/Config.in	Wed Apr  3 21:50:43 2002
+@@ -60,6 +60,7 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++   source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/Makefile	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,23 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/Config.in	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,12 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/br_db.c	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtable_filter.c	Sat Apr 13 21:51:47 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200},
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200},
++  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtable_nat.c	Sat Apr 13 21:54:58 2002
+@@ -0,0 +1,153 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,300},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,200 + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_arp.c	Sat Apr 13 21:45:34 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_ip.c	Sat Apr 13 21:47:18 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_log.c	Sat Apr 13 21:49:45 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_nat.c	Sat Apr 13 21:51:32 2002
+@@ -0,0 +1,118 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (skb_cloned(*pskb)) {
++		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++		if (!nskb)
++			return EBT_DROP;
++		if ((*pskb)->sk)
++			skb_set_owner_w(nskb, (*pskb)->sk);
++		kfree_skb(*pskb);
++		*pskb = nskb;
++	}
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return EBT_ACCEPT;
++}
++
++__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (skb_cloned(*pskb)) {
++		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++		if (!nskb)
++			return EBT_DROP;
++		if ((*pskb)->sk)
++			skb_set_owner_w(nskb, (*pskb)->sk);
++		kfree_skb(*pskb);
++		*pskb = nskb;
++	}
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return EBT_ACCEPT;
++}
++
++int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	return 0;
++}
++
++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
++		return -EINVAL;
++	return 0;
++}
++
++struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtables.c	Sat Apr 13 21:36:18 2002
+@@ -0,0 +1,1168 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size - sizeof(struct ebt_entry_watcher), c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size - sizeof(struct ebt_entry_match), c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		) {
++			if ( (point->bitmask & EBT_SOURCEMAC) &&
++			   FWINV(!!memcmp(point->sourcemac,
++			   ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++			   EBT_ISOURCE) )
++				goto letscontinue;
++
++			if ( (point->bitmask & EBT_DESTMAC) &&
++			   FWINV(!!memcmp(point->destmac,
++			   ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++			   EBT_IDEST) )
++				goto letscontinue;
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size - sizeof(*m)) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size - sizeof(*w)) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size - sizeof(*m));
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w));
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size - sizeof(*t)) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size - sizeof(*t));
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebtables.h	Sat Apr 13 16:06:20 2002
+@@ -0,0 +1,318 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++/* [gs]etsockopt numbers */
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of this struct + size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of this struct + size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of this struct + size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++/* one entry */
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++	       void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size) {                  \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size) {              \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_arp.h	Sat Apr 13 16:11:46 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_ip.h	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_log.h	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_nat.h	Wed Apr  3 21:50:43 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/br_db.h	Sat Apr 13 22:43:11 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff
new file mode 100644
index 0000000..72e80fe
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff
@@ -0,0 +1,3108 @@
+ebtables-v2.0pre3 - 27 April
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/bridge/br_private.h	Sat Apr 27 21:52:48 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre3/include/linux/if_bridge.h	Sat Apr 27 21:39:14 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/core/dev.c	Sat Apr 27 21:05:16 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/bridge/br_input.c	Sat Apr 27 21:05:16 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/netsyms.c	Sat Apr 27 21:05:16 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre3/include/linux/netfilter_bridge.h	Sat Apr 27 21:53:07 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/Makefile	Sat Apr 27 21:05:16 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/Config.in	Sat Apr 27 21:05:16 2002
+@@ -60,6 +60,7 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++   source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/Makefile	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/Config.in	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/br_db.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_filter.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_nat.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_broute.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_redirect.c	Sat Apr 27 22:48:52 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_arp.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_ip.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_log.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_nat.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,106 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtables.c	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,1180 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			if ( (point->bitmask & EBT_SOURCEMAC) &&
++			   FWINV(!!memcmp(point->sourcemac,
++			   ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++			   EBT_ISOURCE) )
++				goto letscontinue;
++
++			if ( (point->bitmask & EBT_DESTMAC) &&
++			   FWINV(!!memcmp(point->destmac,
++			   ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++			   EBT_IDEST) )
++				goto letscontinue;
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebtables.h	Sat Apr 27 21:54:11 2002
+@@ -0,0 +1,330 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_arp.h	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_ip.h	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_log.h	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_nat.h	Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/br_db.h	Sat Apr 27 21:54:40 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff
new file mode 100644
index 0000000..e50fba4
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff
@@ -0,0 +1,3122 @@
+ebtables-v2.0pre4 - 29 April
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/bridge/br_private.h	Mon Apr 29 19:54:04 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre4/include/linux/if_bridge.h	Mon Apr 29 19:54:04 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/core/dev.c	Mon Apr 29 19:54:04 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/bridge/br_input.c	Mon Apr 29 19:54:04 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/netsyms.c	Mon Apr 29 19:54:04 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre4/include/linux/netfilter_bridge.h	Mon Apr 29 19:54:04 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/Makefile	Mon Apr 29 19:54:04 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/Config.in	Mon Apr 29 19:54:04 2002
+@@ -60,6 +60,7 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++   source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/Makefile	Mon Apr 29 19:54:04 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/Config.in	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/br_db.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_filter.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_nat.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_broute.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_redirect.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_arp.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_ip.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_log.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_nat.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,106 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtables.c	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,1180 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			if ( (point->bitmask & EBT_SOURCEMAC) &&
++			   FWINV(!!memcmp(point->sourcemac,
++			   ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++			   EBT_ISOURCE) )
++				goto letscontinue;
++
++			if ( (point->bitmask & EBT_DESTMAC) &&
++			   FWINV(!!memcmp(point->destmac,
++			   ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++			   EBT_IDEST) )
++				goto letscontinue;
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebtables.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,330 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_arp.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_ip.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_log.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_nat.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_redirect.h	Mon Apr 29 20:00:05 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/br_db.h	Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff
new file mode 100644
index 0000000..9537244
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff
@@ -0,0 +1,3133 @@
+ebtables-v2.0pre5 - 01 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Fri May  3 21:15:24 2002
++++ ebt2.0pre5/net/bridge/br_private.h	Wed May  1 15:58:13 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre5/include/linux/if_bridge.h	Wed May  1 15:42:31 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/core/dev.c	Wed May  1 14:51:46 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Fri May  3 21:15:24 2002
++++ ebt2.0pre5/net/bridge/br_input.c	Wed May  1 14:51:46 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/netsyms.c	Wed May  1 14:51:46 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre5/include/linux/netfilter_bridge.h	Wed May  1 15:58:31 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/Makefile	Wed May  1 14:51:46 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Fri May  3 21:15:24 2002
++++ ebt2.0pre5/net/Config.in	Wed May  1 14:51:46 2002
+@@ -60,6 +60,7 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++   source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/Makefile	Wed May  1 14:51:46 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/Config.in	Wed May  1 14:51:46 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/br_db.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_filter.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_nat.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_broute.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_redirect.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_arp.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_ip.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_log.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_nat.c	Wed May  1 14:51:46 2002
+@@ -0,0 +1,106 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtables.c	Fri May  3 20:28:46 2002
+@@ -0,0 +1,1189 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			char hlpmac[6];
++			int j;
++
++			if (point->bitmask & EBT_SOURCEMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_source[j] & point->sourcemsk[j];
++				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++				   ETH_ALEN), EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_dest[j] & point->destmsk[j];
++				if (FWINV(!!memcmp(point->destmac, hlpmac,
++				   ETH_ALEN), EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebtables.h	Thu May  2 19:01:09 2002
+@@ -0,0 +1,332 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_arp.h	Wed May  1 14:51:46 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_ip.h	Wed May  1 14:51:46 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_log.h	Wed May  1 14:51:46 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_nat.h	Wed May  1 14:51:46 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_redirect.h	Wed May  1 14:51:46 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/br_db.h	Wed May  1 16:00:00 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff
new file mode 100644
index 0000000..7aa458c
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff
@@ -0,0 +1,3135 @@
+ebtables-v2.0pre5 - 01 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/bridge/br_private.h	Mon May 20 11:57:27 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre6/include/linux/if_bridge.h	Mon May 20 11:57:27 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/core/dev.c	Mon May 20 11:57:27 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/bridge/br_input.c	Mon May 20 11:57:27 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/netsyms.c	Mon May 20 11:57:27 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre6/include/linux/netfilter_bridge.h	Mon May 20 11:57:27 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/Makefile	Mon May 20 11:57:27 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux/net/Config.in	Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/Config.in	Mon May 20 11:58:15 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/Makefile	Mon May 20 11:57:27 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/Config.in	Mon May 20 11:57:27 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/br_db.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_filter.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_nat.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_broute.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_redirect.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_arp.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_ip.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_log.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_nat.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,106 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtables.c	Mon May 20 11:57:27 2002
+@@ -0,0 +1,1189 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			char hlpmac[6];
++			int j;
++
++			if (point->bitmask & EBT_SOURCEMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_source[j] & point->sourcemsk[j];
++				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++				   ETH_ALEN), EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_dest[j] & point->destmsk[j];
++				if (FWINV(!!memcmp(point->destmac, hlpmac,
++				   ETH_ALEN), EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebtables.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,332 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_arp.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_ip.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_log.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_nat.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_redirect.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/br_db.h	Mon May 20 11:57:27 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff
new file mode 100644
index 0000000..963d6a9
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff
@@ -0,0 +1,3285 @@
+ebtables-v2.0pre7 - 30 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/bridge/br_private.h	Thu May 30 19:59:39 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre7/include/linux/if_bridge.h	Thu May 30 19:45:33 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/core/dev.c	Thu May 30 19:03:46 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/bridge/br_input.c	Thu May 30 19:03:46 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/netsyms.c	Thu May 30 19:03:46 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre7/include/linux/netfilter_bridge.h	Thu May 30 19:59:54 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/Makefile	Thu May 30 19:03:46 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/Config.in	Thu May 30 19:03:46 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/Makefile	Thu May 30 19:03:57 2002
+@@ -0,0 +1,25 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/Config.in	Thu May 30 19:03:57 2002
+@@ -0,0 +1,15 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/br_db.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_filter.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_nat.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_broute.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_redirect.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_arp.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_ip.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_vlan.c	Thu May 30 19:03:57 2002
+@@ -0,0 +1,124 @@
++/*
++ *  ebt_vlan kernelspace
++ *
++ *      Authors:
++ *      Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *      Nick Fedchik <nick@fedchik.org.ua>
++ *
++ *      May, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out,
++			    const void *data,
++			    unsigned int datalen,
++			    const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct vlan_ethhdr *vlanethhdr =
++	    (struct vlan_ethhdr *) skb->mac.raw;
++	unsigned short v_id;
++	unsigned short v_prio;
++
++	/*
++	 * Calculate 802.1Q VLAN ID and Priority 
++	 * Reserved one bit (13) for CFI 
++	 */
++	v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
++	v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++
++	/*
++	 * Checking VLANs 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
++		if (!((infostuff->id == v_id)
++		      ^ !!(infostuff->invflags & EBT_VLAN_ID))) 
++		return 1;
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
++				infostuff->id,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * Checking Priority 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
++		if (!( (infostuff->prio == v_prio) 
++		     ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) 
++		return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
++				infostuff->prio,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * rule matched 
++	 */
++	return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	if (datalen != sizeof (struct ebt_vlan_info))
++		return -EINVAL;
++
++	if (e->ethproto != __constant_htons (ETH_P_8021Q))
++		return -EINVAL;
++
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init (void)
++{
++	printk (KERN_INFO
++		"ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++	if (debug)
++		printk (KERN_DEBUG
++			"ebt_vlan: 802.1Q matching debug is on\n");
++	return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_LICENSE ("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_log.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_nat.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,106 @@
++/*
++ *  ebt_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	int ret;
++	ret = ebt_register_target(&snat);
++	if (ret != 0)
++		return ret;
++	ret = ebt_register_target(&dnat);
++	if (ret == 0)
++		return 0;
++	ebt_unregister_target(&snat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtables.c	Thu May 30 19:03:46 2002
+@@ -0,0 +1,1189 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			char hlpmac[6];
++			int j;
++
++			if (point->bitmask & EBT_SOURCEMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_source[j] & point->sourcemsk[j];
++				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++				   ETH_ALEN), EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_dest[j] & point->destmsk[j];
++				if (FWINV(!!memcmp(point->destmac, hlpmac,
++				   ETH_ALEN), EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	if (!(match = (struct ebt_match *)
++	   list_named_find(&ebt_matches, m->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	m->u.match = match;
++	if (match->check &&
++	   match->check(name, hook, e, m->data,
++	   m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return -EFAULT;
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(watcher = (struct ebt_watcher *)
++	   list_named_find(&ebt_watchers, w->u.name))) {
++		up(&ebt_mutex);
++		return -ENOENT;
++	}
++	w->u.watcher = watcher;
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data,
++	   w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	if (!(target = (struct ebt_target *)
++	   list_named_find(&ebt_targets, t->u.name))) {
++		ret = -ENOENT;
++		up(&ebt_mutex);
++		goto cleanup_watchers;
++	}
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		BUGPRINT("ebt_check_entry gave fault back\n");
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	ret = down_interruptible(&ebt_mutex);
++
++	if (ret != 0)
++		goto free_cleanup;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		ret = -ENOENT;
++		// give some help to the poor user
++		print_string("The table is not present, try insmod\n");
++		goto free_unlock;
++	}
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_cleanup:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_tmp;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++
++	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++		print_string("Table not found, try insmod\n");
++		up(&ebt_mutex);
++		return -EINVAL;
++	}
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebtables.h	Thu May 30 20:00:49 2002
+@@ -0,0 +1,332 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_arp.h	Thu May 30 19:03:46 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_ip.h	Thu May 30 19:03:46 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_vlan.h	Thu May 30 19:03:57 2002
+@@ -0,0 +1,18 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u16 prio;		/* VLAN Priority {0-7} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 - Pirority arg */
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_log.h	Thu May 30 19:03:46 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_nat.h	Thu May 30 19:03:46 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/netfilter_bridge/ebt_redirect.h	Thu May 30 19:03:46 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/include/linux/br_db.h	Thu May 30 20:01:13 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre8_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre8_vs_2.4.18.diff
new file mode 100644
index 0000000..9130d59
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre8_vs_2.4.18.diff
@@ -0,0 +1,3348 @@
+ebtables-v2.0pre8 - 06 June
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Thu Jun  6 19:43:25 2002
++++ ebt2.0pre8/net/bridge/br_private.h	Thu Jun  6 19:10:41 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre8/include/linux/if_bridge.h	Thu Jun  6 19:10:41 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre8/net/core/dev.c	Thu Jun  6 19:06:22 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Thu Jun  6 19:43:25 2002
++++ ebt2.0pre8/net/bridge/br_input.c	Thu Jun  6 19:06:22 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre8/net/netsyms.c	Thu Jun  6 19:06:22 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre8/include/linux/netfilter_bridge.h	Thu Jun  6 19:06:22 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre8/net/Makefile	Thu Jun  6 19:06:22 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Thu Jun  6 19:43:25 2002
++++ ebt2.0pre8/net/Config.in	Thu Jun  6 19:06:22 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/Makefile	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,26 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/Config.in	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/br_db.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebtable_filter.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,93 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table = 
++{ 
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebtable_nat.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,156 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0},
++  {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++  0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebtable_broute.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_redirect.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_arp.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_ip.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_vlan.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,124 @@
++/*
++ *  ebt_vlan kernelspace
++ *
++ *      Authors:
++ *      Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *      Nick Fedchik <nick@fedchik.org.ua>
++ *
++ *      May, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out,
++			    const void *data,
++			    unsigned int datalen,
++			    const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct vlan_ethhdr *vlanethhdr =
++	    (struct vlan_ethhdr *) skb->mac.raw;
++	unsigned short v_id;
++	unsigned short v_prio;
++
++	/*
++	 * Calculate 802.1Q VLAN ID and Priority 
++	 * Reserved one bit (13) for CFI 
++	 */
++	v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
++	v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++
++	/*
++	 * Checking VLANs 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
++		if (!((infostuff->id == v_id)
++		      ^ !!(infostuff->invflags & EBT_VLAN_ID))) 
++		return 1;
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
++				infostuff->id,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * Checking Priority 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
++		if (!( (infostuff->prio == v_prio) 
++		     ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) 
++		return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
++				infostuff->prio,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * rule matched 
++	 */
++	return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	if (datalen != sizeof (struct ebt_vlan_info))
++		return -EINVAL;
++
++	if (e->ethproto != __constant_htons (ETH_P_8021Q))
++		return -EINVAL;
++
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init (void)
++{
++	printk (KERN_INFO
++		"ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++	if (debug)
++		printk (KERN_DEBUG
++			"ebt_vlan: 802.1Q matching debug is on\n");
++	return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_LICENSE ("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_log.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_snat.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebt_dnat.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/net/bridge/netfilter/ebtables.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,1225 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	__u8 verdict;
++
++	read_lock_bh(&table->lock);
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	counter_base = table->private->counters +
++	   cpu_number_map(smp_processor_id()) * table->private->nentries +
++	   table->private->counter_entry[hook];
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ 	for (i = 0; i < nentries; i++) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			char hlpmac[6];
++			int j;
++
++			if (point->bitmask & EBT_SOURCEMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_source[j] & point->sourcemsk[j];
++				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++				   ETH_ALEN), EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_dest[j] & point->destmsk[j];
++				if (FWINV(!!memcmp(point->destmac, hlpmac,
++				   ETH_ALEN), EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict != EBT_CONTINUE) {
++				read_unlock_bh(&table->lock);
++				BUGPRINT("Illegal target while "
++				         "firewalling!!\n");
++				// Try not to get oopsen
++				return NF_DROP;
++			}
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++	}
++
++	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match) 
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hook, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hook, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher) 
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hook, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	if (i != NF_BR_NUMHOOKS) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			BUGPRINT("bad policy\n");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		newinfo->counter_entry[i] = *totalcnt;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++		BUGPRINT("entry offsets point too far\n");
++		return -EINVAL;
++	}
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++		         "bitmask for an entry\n");
++		return -EINVAL;
++	}
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target) 
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict >=
++		   NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hook, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k;
++	int ret;
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		newinfo->hook_entry[i] = NULL;
++		newinfo->counter_entry[i] = 0;
++	}
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))){
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_unlock;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.counter_entry, info->counter_entry,
++	   NF_BR_NUMHOOKS * sizeof(int));
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebtables.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,332 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT   0
++#define EBT_DROP     1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS   3
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// one standard (accept or drop) per hook
++	__u8 policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	__u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	__u8 (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// how many counters in front of the counters bolonging to a chain
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_arp.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_ip.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_vlan.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,18 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u16 prio;		/* VLAN Priority {0-7} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 - Pirority arg */
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_log.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_nat.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/netfilter_bridge/ebt_redirect.h	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8/include/linux/br_db.h	Thu Jun  6 19:12:34 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff
new file mode 100644
index 0000000..08ef3a2
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff
@@ -0,0 +1,3616 @@
+ebtables-v2.0pre9 - 27 June
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h	Fri Jun 28 17:52:15 2002
++++ ebt2.0pre9/net/bridge/br_private.h	Thu Jun 27 20:16:49 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre9/include/linux/if_bridge.h	Thu Jun 27 20:00:23 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/core/dev.c	Thu Jun 27 19:11:50 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Fri Jun 28 17:52:15 2002
++++ ebt2.0pre9/net/bridge/br_input.c	Thu Jun 27 19:11:50 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/netsyms.c	Thu Jun 27 19:11:50 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre9/include/linux/netfilter_bridge.h	Thu Jun 27 20:17:07 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/Makefile	Thu Jun 27 19:11:50 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs :=	bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++	bluetooth atm netlink sched
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux/net/Config.in	Fri Jun 28 17:52:15 2002
++++ ebt2.0pre9/net/Config.in	Thu Jun 27 19:11:50 2002
+@@ -60,6 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/Makefile	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,26 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/Config.in	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/br_db.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,357 @@
++/*
++ *  bridge ethernet protocol database
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  br_db.c, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++	if (*entry == '\0') return 0;
++	if (!device) return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}	
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++	if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++	return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	struct brdb_dbentry *hlp;
++	int i, cpunr;
++	unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++	cpunr = cpu_number_map(smp_processor_id());
++
++	read_lock_bh(&brdb_dblock);
++
++	if (allowdb == BRDB_NODB) {// must be after readlock
++		read_unlock_bh(&brdb_dblock);
++		return NF_ACCEPT;
++	}
++	hlp = flowdb[cpunr];
++	/* search for existing entry */
++	for (i = 0; i < dbnum[cpunr]; i++) {
++		if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++			  !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		hlp++;
++	}
++	/* add new entry to database */
++	if (dbnum[cpunr] == dbsize[cpunr]) {
++		dbsize[cpunr] *= 2;
++		if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++			dbsize[cpunr] /= 2;
++			MEMPRINT("maintaindb && nomemory\n");
++			read_unlock_bh(&brdb_dblock);
++			return NF_ACCEPT;
++		}
++		memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++		vfree(flowdb[cpunr]);
++		flowdb[cpunr] = hlp;
++	}
++
++	hlp = flowdb[cpunr] + dbnum[cpunr];
++	hlp->hook = hook;
++	if (in)
++		strncpy(hlp->in, in->name, IFNAMSIZ);
++	else
++		hlp->in[0] = '\0';
++	if (out)
++		strncpy(hlp->out, out->name, IFNAMSIZ);
++	else
++		hlp->out[0] = '\0';
++	if (ntohs(ethproto) < 1536)
++		hlp->ethproto = IDENTIFY802_3;
++	else
++		hlp->ethproto = ethproto;
++	dbnum[cpunr]++;
++
++	read_unlock_bh(&brdb_dblock);
++
++	return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++	int i, j, nentries = 0, ret;
++	struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++	write_lock_bh(&brdb_dblock);
++	for (i = 0; i < smp_num_cpus; i++)
++		nentries += dbnum[i];
++	if (*len > nentries)
++		return -EINVAL;
++
++	if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++		return -ENOMEM;
++	memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++	end1 = begin + dbnum[0];
++	for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++		point2 = flowdb[i];
++		end2 = end1;
++		for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++			for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++				if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++				    !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++					goto out;/* already exists in a database of another cpu */
++
++			memcpy(end1, point2, sizeof(struct brdb_dbentry));
++			end1++;
++out:
++			point2++;
++		}
++	}
++	write_unlock_bh(&brdb_dblock);
++	i = (int)( (char *)end1 - (char *)begin);
++	*len = i < *len ? i : *len;
++	if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++		ret = -EFAULT;
++	else
++		ret = 0;
++	vfree(begin);
++	return ret;
++}
++
++static int switch_nodb(void){
++	int i;
++
++	if (!flowdb)
++		BUGPRINT("switch_nodb && !flowdb\n");
++	for (i = 0; i < smp_num_cpus; i++)
++		vfree(flowdb[i]);
++	vfree(flowdb);
++	if (!dbsize)
++		BUGPRINT("switch_nodb && !dbsize\n");
++	vfree(dbsize);
++	if (!dbnum)
++		BUGPRINT("switch_nodb && !dbnum\n");
++	vfree(dbnum);
++	flowdb = NULL;
++	allowdb = BRDB_NODB;
++	return 0;
++}
++
++static int switch_db(void)
++{
++	int i, j;
++
++	if (flowdb) BUGPRINT("switch_db && flowdb\n");
++	if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++		MEMPRINT("switch_db && nomemory\n");
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < smp_num_cpus; i++)
++		if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++			goto sw_free1;
++		else
++			memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++	if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free2;
++
++	if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++		goto sw_free3;
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		dbnum[i] = 0;
++		dbsize[i] = INITIAL_DBSIZE;
++	}
++	allowdb = BRDB_DB;
++	return 0;
++
++sw_free3:
++	MEMPRINT("switch_db && nomemory2\n");
++	vfree(dbnum);
++	dbnum = NULL;
++sw_free2:
++	MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++	MEMPRINT("switch_db && nomemory4\n");
++	for (j = 0; j<i; j++)
++		vfree(flowdb[j]);
++	vfree(flowdb);
++	allowdb = BRDB_NODB;
++	return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++	int ret;
++	__u16 adb;
++	switch(cmd) {
++	case BRDB_SO_SET_ALLOWDB:
++		if (len != sizeof(__u16)) {
++			ret = -EINVAL;
++			break;
++		}
++	 	if (copy_from_user(&adb, user, len) != 0) {
++			ret = -EFAULT;
++	 		break;
++		}
++		if (adb != BRDB_DB && adb != BRDB_NODB) {
++			ret = -EINVAL;
++			break;
++		}
++		write_lock_bh(&brdb_dblock);
++		if (adb == allowdb) {
++			ret = 0;
++			write_unlock_bh(&brdb_dblock);
++			break;
++		}
++		if (allowdb == BRDB_DB)
++			ret = switch_nodb();
++		else
++			ret = switch_db();
++		write_unlock_bh(&brdb_dblock);
++		break;
++
++	default:
++		ret = -EINVAL;
++	}
++	return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	struct brdb_dbinfo help2;
++	int i, ret;
++	switch(cmd) {
++	case BRDB_SO_GET_DBINFO:
++		if (sizeof(struct brdb_dbinfo) != *len)
++			return -EINVAL;
++		write_lock_bh(&brdb_dblock);
++		/* 0 == no database
++		 * i-1 == number of entries (if database)
++		 */
++		if (allowdb == BRDB_NODB)
++			help2.nentries = 0;
++		else {
++			help2.nentries = 1;
++			for (i = 0; i < smp_num_cpus; i++)
++				help2.nentries += dbnum[i];
++		}
++		write_unlock_bh(&brdb_dblock);
++		if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++			ret = -EFAULT;
++		else
++			ret = 0;
++		break;
++
++	case BRDB_SO_GET_DB:
++		if (*len == 0 || allowdb == BRDB_NODB)
++			return -EINVAL;
++		ret = copy_db(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++    BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL  };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++	{ { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++		return ret;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++		goto clean0;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++		goto clean1;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++		goto clean2;
++
++	if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++		goto clean3;
++
++	/* Register setsockopt */
++	if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++		goto clean4;
++	
++	rwlock_init(&brdb_dblock);
++	printk("Bridge ethernet database registered\n");
++	return ret;
++
++clean4:		nf_unregister_hook(&brdb_br_ops[4]);
++clean3:		nf_unregister_hook(&brdb_br_ops[3]);
++clean2:		nf_unregister_hook(&brdb_br_ops[2]);
++clean1:		nf_unregister_hook(&brdb_br_ops[1]);
++clean0:		nf_unregister_hook(&brdb_br_ops[0]);
++
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_hook(&brdb_br_ops[4]);
++	nf_unregister_hook(&brdb_br_ops[3]);
++	nf_unregister_hook(&brdb_br_ops[2]);
++	nf_unregister_hook(&brdb_br_ops[1]);
++	nf_unregister_hook(&brdb_br_ops[0]);
++	nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_filter.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,92 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_nat.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,155 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++        hard_header_len:        ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = NULL;
++	return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	(*pskb)->physindev = &__fake_net_device;
++	return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	// this is a routed packet
++	if ((*pskb)->physindev == NULL)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev != &__fake_net_device)
++		printk("ebtables (br_nat_src): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	// this is a bridged packet
++	if ((*pskb)->physindev == &__fake_net_device)
++		return NF_ACCEPT;
++	if ((*pskb)->physindev)
++		printk("ebtables (br_nat_src_route): physindev hack "
++		       "doesn't work - BUG\n");
++
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_broute.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTE", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_redirect.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_arp.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,107 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return 1;
++	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return 1;
++
++	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		__u32 arp_len = sizeof(struct arphdr) +
++		   (2*(((*skb).nh.arph)->ar_hln)) +
++		   (2*(((*skb).nh.arph)->ar_pln));
++		__u32 dst;
++		__u32 src;
++
++ 		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return 1;
++		// IPV4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++			return 1;
++
++		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   EBT_ARP_SRC_IP))
++				return 1;
++		}
++
++		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   EBT_ARP_DST_IP))
++				return 1;
++		}
++	}
++	return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
++	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (infostuff->bitmask & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_ip.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,81 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *data,
++	       unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (infostuff->bitmask & EBT_IP_TOS &&
++	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return 1;
++	if (infostuff->bitmask & EBT_IP_SOURCE &&
++	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++	   infostuff->saddr, EBT_IP_SOURCE))
++		return 1;
++	if ((infostuff->bitmask & EBT_IP_DEST) &&
++	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++	   infostuff->daddr, EBT_IP_DEST))
++		return 1;
++	return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++	if (datalen != sizeof(struct ebt_ip_info)) {
++		return -EINVAL;
++	}
++	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
++	    e->ethproto != __constant_htons(ETH_P_IP) ||
++	    e->invflags & EBT_IPROTO)
++	{
++		return -EINVAL;
++	}
++	if (infostuff->bitmask & ~EBT_IP_MASK) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_vlan.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_vlan kernelspace
++ *
++ *      Authors:
++ *      Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *      Nick Fedchik <nick@fedchik.org.ua>
++ *
++ *      June, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++#define MODULE_VERSION "0.2"
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out,
++			    const void *data,
++			    unsigned int datalen,
++			    const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct vlan_ethhdr *vlanethhdr =
++	    (struct vlan_ethhdr *) skb->mac.raw;
++	unsigned short v_id;
++	unsigned short v_prio;
++	unsigned short v_TCI;
++
++	/*
++	 * Calculate 802.1Q VLAN ID and user_priority from 
++	 * Tag Control Information (TCI) field.
++	 * Reserved one bit (13) for CFI (Canonical Format Indicator)
++	 */
++	v_TCI = ntohs (vlanethhdr->h_vlan_TCI);
++	v_id = v_TCI & 0xFFF;
++	v_prio = v_TCI >> 13;
++
++	/*
++	 * Checking VLANs 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
++		if (!((infostuff->id == v_id)
++		      ^ !!(infostuff->invflags & EBT_VLAN_ID)))
++			return 1;
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
++				(infostuff->
++				 invflags & EBT_VLAN_ID) ? "!" : "",
++				infostuff->id, infostuff->bitmask);
++	}
++	/*
++	 * Checking User Priority 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
++		if (!((infostuff->prio == v_prio)
++		      ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
++			return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++				(infostuff->
++				 invflags & EBT_VLAN_PRIO) ? "!" : "",
++				infostuff->prio, infostuff->bitmask);
++	}
++	/*
++	 * Checking for Encapsulated proto
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ENCAP) {	/* Is VLAN Encap parsed? */
++		if (!
++		    ((infostuff->encap ==
++		      vlanethhdr->h_vlan_encapsulated_proto)
++		     ^ !!(infostuff->invflags & EBT_VLAN_ENCAP)))
++			return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched encap=%s%2.4X (mask=%X)\n",
++				(infostuff->
++				 invflags & EBT_VLAN_ENCAP) ? "!" : "",
++				ntohs (infostuff->encap),
++				infostuff->bitmask);
++	}
++
++	/*
++	 * rule matched 
++	 */
++	return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	if (datalen != sizeof (struct ebt_vlan_info))
++		return -EINVAL;
++
++	if (e->ethproto != __constant_htons (ETH_P_8021Q))
++		return -EINVAL;
++
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_vlan_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init (void)
++{
++	printk (KERN_INFO
++		"ebt_vlan: 802.1Q VLAN matching module for EBTables "
++		MODULE_VERSION "\n");
++	if (debug)
++		printk (KERN_DEBUG
++			"ebt_vlan: 802.1Q rule matching debug is on\n");
++	return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_log.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,111 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (loginfo->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (loginfo->loglevel >= 8)
++		return -EINVAL;
++	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen,
++   const struct ebt_counter *c)
++{
++	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + loginfo->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	// max length: 29 + 10 + 2 * 16
++	printk("%s IN=%s OUT=%s ",
++	       loginfo->prefix,
++	       in ? in->name : "",
++	       out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 31
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p,
++			       i == ETH_ALEN - 1
++			       ? ' ':':');// length: 29
++	}
++	// length: 14
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		// max length: 46
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		// max length: 26
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		// max length: 40
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_snat.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_dnat.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtables.c	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,1453 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size, c);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++	    const struct sk_buff *skb,
++	    const struct net_device *in,
++	    const struct net_device *out,
++	    const struct ebt_counter *c)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, j, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++
++	read_lock_bh(&table->lock);
++	cs = table->private->chainstack;
++	chaininfo = table->private->hook_entry[hook];
++	nentries = table->private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	#define cb_base table->private->counters + \
++	   cpu_number_map(smp_processor_id()) * table->private->nentries
++	counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
++	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++ 	while (i < nentries) {
++		if ( ( point->bitmask & EBT_NOPROTO ||
++		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++		      EBT_IPROTO)
++		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
++		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++		) {
++			if (point->bitmask & EBT_SOURCEMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_source[j] ^ point->sourcemac[j]) &
++					   point->sourcemsk[j];
++				if (FWINV(!!verdict, EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				verdict = 0;
++				for (j = 0; j < 6; j++)
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_dest[j] ^ point->destmac[j]) &
++					   point->destmsk[j];
++				if (FWINV(!!verdict, EBT_IDEST) )
++					goto letscontinue;
++			}
++
++			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++			   out, counter_base + i) != 0)
++				goto letscontinue;
++
++			// increase counter
++			(*(counter_base + i)).pcnt++;
++
++			// these should only watch: not modify, nor tell us
++			// what to do with the packet
++			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++			   out, counter_base + i);
++
++			t = (struct ebt_entry_target *)
++			   (((char *)point) + point->target_offset);
++			// standard target
++			if (!t->u.target->target)
++				verdict =
++				   ((struct ebt_standard_target *)t)->verdict;
++			else
++				verdict = t->u.target->target(pskb, hook,
++				   in, out, t->data, t->target_size);
++			if (verdict == EBT_ACCEPT) {
++				read_unlock_bh(&table->lock);
++				return NF_ACCEPT;
++			}
++			if (verdict == EBT_DROP) {
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			if (verdict == EBT_RETURN) {
++letsreturn:
++				if (sp == 0)
++					// act like this is EBT_CONTINUE
++					goto letscontinue;
++				sp--;
++				// put all the local variables right
++				i = cs[sp].n;
++				chaininfo = cs[sp].chaininfo;
++				nentries = chaininfo->nentries;
++				point = cs[sp].e;
++				counter_base = cb_base +
++				   chaininfo->counter_offset;
++				continue;
++			}
++			if (verdict == EBT_CONTINUE)
++				goto letscontinue;
++			if (verdict < 0) {
++				BUGPRINT("bogus standard verdict\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			// jump to a udc
++			cs[sp].n = i + 1;
++			cs[sp].chaininfo = chaininfo;
++			cs[sp].e = (struct ebt_entry *)
++			   (((char *)point) + point->next_offset);
++			i = 0;
++			chaininfo = (struct ebt_entries *) (base + verdict);
++			if (chaininfo->distinguisher) {
++				BUGPRINT("jump to non-chain\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			nentries = chaininfo->nentries;
++			point = (struct ebt_entry *)chaininfo->data;
++			counter_base = cb_base + chaininfo->counter_offset;
++			sp++;
++			continue;
++		}
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match) 
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher) 
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset > e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data,
++	   t->target_size) != 0) {
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if (e->bitmask == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// we just don't trust anything
++	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++   struct ebt_counter *counters)
++{
++	int i, cpu, counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, info->counters,
++	   sizeof(struct ebt_counter) * info->nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = cpu * info->nentries;
++		for (i = 0; i < info->nentries; i++)
++			counters[i].pcnt +=
++			   info->counters[counter_base + i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++ 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (tmp.nentries) {
++		newinfo->counters = (struct ebt_counter *)vmalloc(
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++		if (!newinfo->counters) {
++			ret = -ENOMEM;
++			goto free_newinfo;
++		}
++		memset(newinfo->counters, 0,
++		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_counters;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_unlock;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++		
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private, counterstmp);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up his counter
++	// allocation. Only reason why I do this is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->counters)
++		vfree(table->counters);
++	if (table->chainstack)
++		vfree(table->chainstack);
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info));
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (table->table->nentries) {
++		newinfo->counters = (struct ebt_counter *)
++		   vmalloc(table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++		if (!newinfo->counters)
++			goto free_entries;
++		memset(newinfo->counters, 0, table->table->nentries *
++		   sizeof(struct ebt_counter) * smp_num_cpus);
++	}
++	else
++		newinfo->counters = NULL;
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_counters;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_counters;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_counters:
++	if (newinfo->counters)
++		vfree(newinfo->counters);
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
++free_entries:
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->counters)
++		vfree(table->private->counters);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack)
++		vfree(table->private->chainstack);
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Updata_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp = ubase - base + (char *)e + e->target_offset;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++	struct ebt_replace tmp;
++	struct ebt_table_info *info = t->private;
++	struct ebt_counter *counterstmp;
++	int i;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + info->entries_size +
++	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != info->nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != info->entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != info->nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			BUGPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(info, counterstmp);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   info->nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// make userspace's life easier
++	memcpy(tmp.hook_entry, info->hook_entry,
++	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++		   (info->hook_entry[i])) - info->entries + tmp.entries);
++	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++	   ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		tmp.nentries = t->private->nentries;
++		tmp.entries_size = t->private->entries_size;
++		// userspace needs this to check the chain names
++		tmp.valid_hooks = t->valid_hooks;
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++		ret = copy_everything_to_user(t, user, len);
++		up(&ebt_mutex);
++		break;			
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	print_string("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebtables.h	Thu Jun 27 20:18:07 2002
+@@ -0,0 +1,345 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, April, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero (including userspace).
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	__u32 distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	__u32 nentries;
++	// entry list
++	__u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	__u32 bitmask;
++	__u32 invflags;
++	__u16 ethproto;
++	// the physical in-dev
++	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
++	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
++	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
++	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	__u16 watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	__u16 target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	__u16 next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen, const struct ebt_counter *c);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb,
++	       unsigned int hooknr,
++	       const struct net_device *in,
++	       const struct net_device *out,
++	       const void *targetdata,
++	       unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	struct ebt_counter *counters;
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack *chainstack;
++	char *entries;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++		 __i += __entry->next_offset;               \
++		else                                        \
++		 __i += sizeof(struct ebt_entries);         \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_arp.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	__u16 htype;
++	__u16 ptype;
++	__u16 opcode;
++	__u32 saddr;
++	__u32 smsk;
++	__u32 daddr;
++	__u32 dmsk;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_ip.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	__u32 saddr;
++	__u32 daddr;
++	__u32 smsk;
++	__u32 dmsk;
++	__u8  tos;
++	__u8  protocol;
++	__u8  bitmask;
++	__u8  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_vlan.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u8 prio;		/* VLAN User Priority {0-7} */
++	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_log.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	__u8 loglevel;
++	__u8 prefix[EBT_LOG_PREFIX_SIZE];
++	__u32 bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_nat.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_redirect.h	Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/br_db.h	Thu Jun 27 20:18:35 2002
+@@ -0,0 +1,53 @@
++/*
++ *  bridge ethernet protocol filter
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *	br_db.h,v 1.1 2001/04/16
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL            135
++
++#define BRDB_SO_SET_ALLOWDB      (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX          (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO       (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB           (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX          (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB   1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++	__u32 nentries;
++};
++
++struct brdb_dbentry {
++	__u8 in[IFNAMSIZ];
++	__u8 out[IFNAMSIZ];
++	__u16 ethproto;
++	__u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.diff b/kernel/patches/base-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.diff
new file mode 100644
index 0000000..fc58376
--- /dev/null
+++ b/kernel/patches/base-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.diff
@@ -0,0 +1,3496 @@
+ebtables-v2.0.001 vs 2.4.20-pre7 - 17 September 2002
+
+--- linux-2.4.20-pre7/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre7-ebt/net/bridge/br_private.h	Sun Sep 29 21:22:56 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.4.20-pre7/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.20-pre7-ebt/include/linux/if_bridge.h	Sun Sep 29 21:22:56 2002
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.20-pre7/net/core/dev.c	Sun Sep 29 21:30:55 2002
++++ linux-2.4.20-pre7-ebt/net/core/dev.c	Sun Sep 29 21:22:56 2002
+@@ -1392,7 +1392,7 @@ void net_call_rx_atomic(void (*fn)(void)
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1409,7 +1409,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1469,7 +1468,12 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+--- linux-2.4.20-pre7/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-ebt/net/bridge/br_input.c	Sun Sep 29 21:24:37 2002
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,31 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.20-pre7/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-ebt/net/bridge/br_forward.c	Sun Sep 29 21:22:56 2002
+@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ 			__br_forward_finish);
+ }
+--- linux-2.4.20-pre7/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre7-ebt/net/bridge/br.c	Sun Sep 29 21:22:56 2002
+@@ -28,6 +28,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -74,7 +76,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20-pre7/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/Makefile	Sun Sep 29 21:22:56 2002
+@@ -7,6 +7,8 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux-2.4.20-pre7/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge.h	Sun Sep 29 21:22:56 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+--- linux-2.4.20-pre7/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-ebt/net/Makefile	Sun Sep 29 21:22:56 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@ subdir-$(CONFIG_IPV6)		+= ipv6
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux-2.4.20-pre7/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-ebt/net/Config.in	Sun Sep 29 21:22:56 2002
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/Makefile	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,27 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/Config.in	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebtable_filter.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebtable_nat.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebtable_broute.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_mark.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_mark_m.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_redirect.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_arp.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_ip.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
++	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_vlan.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (info->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) info->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     info->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (info->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_log.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_snat.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_dnat.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebtables.c	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,1484 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++)
++			counters[i].pcnt += counter_base[i].pcnt;
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++)
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebtables.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,358 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++#ifdef __KERNEL__
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_arp.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_ip.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_vlan.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_log.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_nat.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_redirect.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_mark_m.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-ebt/include/linux/netfilter_bridge/ebt_mark_t.h	Sun Sep 29 21:22:56 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/base-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.diff b/kernel/patches/base-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.diff
new file mode 100644
index 0000000..d33378b
--- /dev/null
+++ b/kernel/patches/base-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.diff
@@ -0,0 +1,3569 @@
+ebtables-v2.0.002 vs 2.4.20-pre7 - 17 October 2002
+
+--- linux-2.4.20-pre7/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/br_private.h	Thu Oct 17 22:40:38 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.4.20-pre7/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.20-pre7-bcnt/include/linux/if_bridge.h	Thu Oct 17 22:28:23 2002
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.20-pre7/net/core/dev.c	Thu Oct 17 23:42:53 2002
++++ linux-2.4.20-pre7-bcnt/net/core/dev.c	Thu Oct 17 20:31:28 2002
+@@ -1392,7 +1392,7 @@ void net_call_rx_atomic(void (*fn)(void)
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1409,7 +1409,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1469,7 +1468,12 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+--- linux-2.4.20-pre7/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/br_input.c	Thu Oct 17 20:31:28 2002
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,31 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.20-pre7/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/br_forward.c	Thu Oct 17 20:31:28 2002
+@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ 			__br_forward_finish);
+ }
+--- linux-2.4.20-pre7/net/bridge/br.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/br.c	Thu Oct 17 20:31:28 2002
+@@ -28,6 +28,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -74,7 +76,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20-pre7/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/Makefile	Thu Oct 17 20:31:28 2002
+@@ -7,6 +7,8 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux-2.4.20-pre7/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge.h	Thu Oct 17 22:40:51 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+--- linux-2.4.20-pre7/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-bcnt/net/Makefile	Thu Oct 17 20:31:29 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@ subdir-$(CONFIG_IPV6)		+= ipv6
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux-2.4.20-pre7/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre7-bcnt/net/Config.in	Thu Oct 17 20:31:29 2002
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/Makefile	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,27 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/Config.in	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebtable_filter.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebtable_nat.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebtable_broute.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_mark.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_mark_m.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_redirect.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_arp.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_ip.c	Thu Oct 17 23:22:58 2002
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_vlan.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (info->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) info->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     info->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (info->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
++	return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_log.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_snat.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_dnat.c	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebtables.c	Thu Oct 17 21:58:08 2002
+@@ -0,0 +1,1489 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebtables.h	Thu Oct 17 22:41:32 2002
+@@ -0,0 +1,359 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_arp.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_ip.h	Thu Oct 17 23:22:45 2002
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_vlan.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_log.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_nat.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_redirect.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_mark_m.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_mark_t.h	Thu Oct 17 20:31:29 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/base-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.diff b/kernel/patches/base-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.diff
new file mode 100644
index 0000000..311d2c7
--- /dev/null
+++ b/kernel/patches/base-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.diff
@@ -0,0 +1,3510 @@
+ebtables-v2.0 vs 2.4.20 - 06 December 2002
+
+--- linux-2.4.20/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.20-patch/net/bridge/br_private.h	Fri Dec  6 23:26:33 2002
+@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux-2.4.20/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.20-patch/include/linux/if_bridge.h	Fri Dec  6 23:21:10 2002
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.20/net/core/dev.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.20-patch/net/core/dev.c	Fri Dec  6 22:55:43 2002
+@@ -1380,7 +1380,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1397,7 +1397,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1457,7 +1456,12 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+--- linux-2.4.20/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/bridge/br_input.c	Fri Dec  6 22:59:15 2002
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,31 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.20/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/bridge/br_forward.c	Fri Dec  6 23:00:12 2002
+@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ 			__br_forward_finish);
+ }
+--- linux-2.4.20/net/bridge/br.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.20-patch/net/bridge/br.c	Fri Dec  6 23:02:44 2002
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -74,7 +76,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.20-patch/net/bridge/Makefile	Fri Dec  6 23:03:36 2002
+@@ -7,6 +7,8 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux-2.4.20/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.20-patch/include/linux/netfilter_bridge.h	Fri Dec  6 23:26:35 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
+ 
+ #endif
+--- linux-2.4.20/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/Makefile	Fri Dec  6 23:06:20 2002
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.20/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-patch/net/Config.in	Fri Dec  6 23:15:28 2002
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/Makefile	Fri Dec  6 23:08:32 2002
+@@ -0,0 +1,27 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/Config.in	Fri Dec  6 23:08:26 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebtable_filter.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebtable_nat.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebtable_broute.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_mark.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_mark_m.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_redirect.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_arp.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,102 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_ip.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_vlan.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG "ebt_vlan: " __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof(struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_log.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,100 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	if (skb->dev->hard_header_len) {
++		int i;
++		unsigned char *p = (skb->mac.ethernet)->h_source;
++
++		printk("MAC source = ");
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++		printk("MAC dest = ");
++		p = (skb->mac.ethernet)->h_dest;
++		for (i = 0; i < ETH_ALEN; i++,p++)
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++	}
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++	}
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_snat.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebt_dnat.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/net/bridge/netfilter/ebtables.c	Fri Dec  6 23:08:19 2002
+@@ -0,0 +1,1489 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk("Ebtables v2.0 registered");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebtables.h	Fri Dec  6 23:26:40 2002
+@@ -0,0 +1,359 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_arp.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_ip.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_vlan.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_log.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_nat.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_redirect.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_mark_m.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Sat May 18 12:04:21 2002
++++ linux-2.4.20-patch/include/linux/netfilter_bridge/ebt_mark_t.h	Fri Dec  6 23:10:35 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.21.diff b/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.21.diff
new file mode 100644
index 0000000..b687cb3
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.21.diff
@@ -0,0 +1,5608 @@
+--- linux-2.4.21/net/bridge/br_private.h	Mon Feb 25 20:38:14 2002
++++ linux-2.4.21-ebt-brnf-2/net/bridge/br_private.h	Thu Jul 24 01:55:39 2003
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -176,6 +179,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg1,
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
++
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
+ 
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+--- linux-2.4.21/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ linux-2.4.21-ebt-brnf-2/include/linux/if_bridge.h	Thu Jul 24 01:40:57 2003
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.21/net/core/dev.c	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/core/dev.c	Thu Jul 24 00:01:08 2003
+@@ -1424,7 +1424,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1441,7 +1441,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1461,6 +1460,9 @@ int netif_receive_skb(struct sk_buff *sk
+ 	struct packet_type *ptype, *pt_prev;
+ 	int ret = NET_RX_DROP;
+ 	unsigned short type = skb->protocol;
++#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
++	struct net_device* rx_dev = skb->dev;
++#endif
+ 
+ 	if (skb->stamp.tv_sec == 0)
+ 		do_gettimeofday(&skb->stamp);
+@@ -1497,11 +1499,19 @@ int netif_receive_skb(struct sk_buff *sk
+ 	if (skb->dev->divert && skb->dev->divert->divert)
+ 		ret = handle_diverter(skb);
+ #endif /* CONFIG_NET_DIVERT */
+-			
++
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+-	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++        skb->protocol != __constant_ntohs(ETH_P_8021Q) &&
++#endif
++        br_handle_frame_hook != NULL) {
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+@@ -1526,6 +1536,22 @@ int netif_receive_skb(struct sk_buff *sk
+ 		} else {
+ 			ret = pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
++#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
++		if (skb->protocol == __constant_ntohs(ETH_P_8021Q) && !skb->dev) {
++		    skb->dev = rx_dev;
++
++		    if (skb->dev->br_port != NULL &&
++				br_handle_frame_hook != NULL) {
++				int ret;
++
++				ret = handle_bridge(skb, NULL);
++				if (br_handle_frame_hook(skb) == 0)
++					return ret;
++			}
++
++			kfree_skb(skb);
++		}
++#endif
+ 	} else {
+ 		kfree_skb(skb);
+ 		/* Jamal, now you will not able to escape explaining
+@@ -1897,7 +1923,7 @@ static int dev_proc_stats(char *buffer, 
+  *	are adjusted, %RTM_NEWLINK is sent to the routing socket and the
+  *	function returns zero.
+  */
+- 
++
+ int netdev_set_master(struct net_device *slave, struct net_device *master)
+ {
+ 	struct net_device *old = slave->master;
+--- linux-2.4.21/net/bridge/br_input.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.21-ebt-brnf-2/net/bridge/br_input.c	Thu Jul 24 00:01:08 2003
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.21/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.21-ebt-brnf-2/net/bridge/br_forward.c	Thu Jul 24 00:01:08 2003
+@@ -30,18 +30,22 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +53,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.21/net/bridge/br.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.21-ebt-brnf-2/net/bridge/br.c	Thu Jul 24 00:01:08 2003
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.21/net/bridge/Makefile	Fri Dec 29 23:07:24 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/Makefile	Thu Jul 24 00:01:08 2003
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.21/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge.h	Thu Jul 24 01:55:56 2003
+@@ -1,11 +1,14 @@
+ #ifndef __LINUX_BRIDGE_NETFILTER_H
+ #define __LINUX_BRIDGE_NETFILTER_H
+ 
+-/* bridge-specific defines for netfilter. 
++/* bridge-specific defines for netfilter.
+  */
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +21,49 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
++#ifdef __KERNEL__
+ 
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
++
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
++
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.21/net/Makefile	Sat Aug  3 02:39:46 2002
++++ linux-2.4.21-ebt-brnf-2/net/Makefile	Thu Jul 24 00:01:08 2003
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -23,6 +24,12 @@ subdir-$(CONFIG_IPV6)		+= ipv6
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+ 
+--- linux-2.4.21/net/Config.in	Sat Aug  3 02:39:46 2002
++++ linux-2.4.21-ebt-brnf-2/net/Config.in	Thu Jul 24 00:01:09 2003
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/Makefile	Thu Jul 24 00:26:55 2003
+@@ -0,0 +1,30 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/Config.in	Thu Jul 24 00:28:56 2003
+@@ -0,0 +1,19 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebtable_filter.c	Thu Jul 24 00:26:42 2003
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebtable_nat.c	Thu Jul 24 00:26:43 2003
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebtable_broute.c	Thu Jul 24 00:26:42 2003
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_802_3.c	Thu Jul 24 00:26:39 2003
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_mark.c	Thu Jul 24 00:26:41 2003
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_mark_m.c	Thu Jul 24 00:26:41 2003
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_pkttype.c	Thu Jul 24 00:26:41 2003
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_stp.c	Thu Jul 24 00:26:42 2003
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_redirect.c	Thu Jul 24 00:26:41 2003
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_arp.c	Thu Jul 24 00:26:40 2003
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_ip.c	Thu Jul 24 00:26:40 2003
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_vlan.c	Thu Jul 24 11:53:27 2003
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_log.c	Thu Jul 24 00:26:41 2003
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_snat.c	Thu Jul 24 00:26:42 2003
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebt_dnat.c	Thu Jul 24 00:26:40 2003
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/netfilter/ebtables.c	Thu Jul 24 00:26:43 2003
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebtables.h	Fri Jul 25 16:34:44 2003
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_replace)-1)) & \
++		     ~(__alignof__(struct ebt_replace)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_802_3.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_arp.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_ip.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_pkttype.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_stp.h	Thu Jul 24 00:29:18 2003
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_vlan.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_log.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_nat.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_redirect.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_mark_m.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_bridge/ebt_mark_t.h	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.21/include/linux/netfilter.h	Thu Nov 22 20:47:48 2001
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter.h	Thu Jul 24 01:41:40 2003
+@@ -117,28 +117,34 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+-int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
++int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt,
+ 		  int len);
+ int nf_getsockopt(struct sock *sk, int pf, int optval, char *opt,
+ 		  int *len);
+ 
+ /* Packet queuing */
+-typedef int (*nf_queue_outfn_t)(struct sk_buff *skb, 
++typedef int (*nf_queue_outfn_t)(struct sk_buff *skb,
+                                 struct nf_info *info, void *data);
+-extern int nf_register_queue_handler(int pf, 
++extern int nf_register_queue_handler(int pf,
+                                      nf_queue_outfn_t outfn, void *data);
+ extern int nf_unregister_queue_handler(int pf);
+ extern void nf_reinject(struct sk_buff *skb,
+--- linux-2.4.21/include/linux/netfilter_ipv4.h	Mon Feb 25 20:38:13 2002
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_ipv4.h	Thu Jul 24 01:41:40 2003
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.21/include/linux/skbuff.h	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/include/linux/skbuff.h	Thu Jul 24 01:15:15 2003
+@@ -92,6 +92,17 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -204,6 +215,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -278,7 +292,7 @@ static inline struct sk_buff *skb_get(st
+  * If users==1, we are the only owner and are can avoid redundant
+  * atomic change.
+  */
+- 
++
+ /**
+  *	kfree_skb - free an sk_buff
+  *	@skb: buffer to free
+@@ -286,7 +300,7 @@ static inline struct sk_buff *skb_get(st
+  *	Drop a reference to the buffer and free it if the usage count has
+  *	hit zero.
+  */
+- 
++
+ static inline void kfree_skb(struct sk_buff *skb)
+ {
+ 	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+@@ -1165,6 +1179,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.21/net/core/netfilter.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.21-ebt-brnf-2/net/core/netfilter.c	Thu Jul 24 00:30:02 2003
+@@ -1,4 +1,4 @@
+-/* netfilter.c: look after the filters for various protocols. 
++/* netfilter.c: look after the filters for various protocols.
+  * Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
+  *
+  * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -365,7 +370,7 @@ static unsigned int nf_iterate(struct li
+ 			break;
+ 
+ 		default:
+-			NFDEBUG("Evil return from %p(%u).\n", 
++			NFDEBUG("Evil return from %p(%u).\n",
+ 				elem->hook, hook);
+ #endif
+ 		}
+@@ -374,7 +379,7 @@ static unsigned int nf_iterate(struct li
+ }
+ 
+ int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
+-{      
++{
+ 	int ret;
+ 
+ 	br_write_lock_bh(BR_NETPROTO_LOCK);
+@@ -400,12 +405,12 @@ int nf_unregister_queue_handler(int pf)
+ 	return 0;
+ }
+ 
+-/* 
+- * Any packet that leaves via this function must come back 
++/*
++ * Any packet that leaves via this function must come back
+  * through nf_reinject().
+  */
+-static void nf_queue(struct sk_buff *skb, 
+-		     struct list_head *elem, 
++static void nf_queue(struct sk_buff *skb,
++		     struct list_head *elem,
+ 		     int pf, unsigned int hook,
+ 		     struct net_device *indev,
+ 		     struct net_device *outdev,
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -428,18 +437,31 @@ static void nf_queue(struct sk_buff *skb
+ 		return;
+ 	}
+ 
+-	*info = (struct nf_info) { 
++	*info = (struct nf_info) {
+ 		(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
+ 
+ 	/* Bump dev refs so they don't vanish while packet is out */
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -528,9 +559,9 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	if (verdict == NF_ACCEPT) {
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+-				     &skb, info->hook, 
++				     &skb, info->hook,
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+@@ -539,20 +570,19 @@ void nf_reinject(struct sk_buff *skb, st
+ 		break;
+ 
+ 	case NF_QUEUE:
+-		nf_queue(skb, elem, info->pf, info->hook, 
++		nf_queue(skb, elem, info->pf, info->hook,
+ 			 info->indev, info->outdev, info->okfn);
+ 		break;
+-
+-	case NF_DROP:
+-		kfree_skb(skb);
+-		break;
+ 	}
+ 	br_read_unlock_bh(BR_NETPROTO_LOCK);
+ 
+ 	/* Release those devices we held, or Alexey will kill me. */
+ 	if (info->indev) dev_put(info->indev);
+ 	if (info->outdev) dev_put(info->outdev);
+-	
++
++	if (verdict == NF_DROP)
++		kfree_skb(skb);
++
+ 	kfree(info);
+ 	return;
+ }
+--- linux-2.4.21/net/core/skbuff.c	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/core/skbuff.c	Thu Jul 24 00:01:09 2003
+@@ -245,6 +245,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -325,6 +328,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -391,6 +397,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -403,6 +412,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -436,6 +448,10 @@ static void copy_skb_header(struct sk_bu
+ 	nf_conntrack_get(new->nfct);
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
++#endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
+ #endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+--- linux-2.4.21/net/ipv4/netfilter/ip_tables.c	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/netfilter/ip_tables.c	Thu Jul 24 00:01:09 2003
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.21/net/ipv4/ip_output.c	Fri Nov 29 00:53:15 2002
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/ip_output.c	Thu Jul 24 00:01:09 2003
+@@ -879,6 +879,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.21/net/ipv4/netfilter/ipt_LOG.c	Mon Feb 25 20:38:14 2002
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/netfilter/ipt_LOG.c	Thu Jul 24 00:01:09 2003
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.21/net/ipv4/netfilter/Makefile	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/netfilter/Makefile	Thu Jul 24 00:01:09 2003
+@@ -84,6 +84,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+@@ -100,6 +102,7 @@ obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt
+ 
+ # generic ARP tables
+ obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o
++obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o
+ 
+ # just filtering instance of ARP tables for now
+ obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
+--- linux-2.4.21/net/ipv4/netfilter/Config.in	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/netfilter/Config.in	Thu Jul 24 00:01:09 2003
+@@ -43,6 +43,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/bridge/br_netfilter.c	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,636 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bdschuym@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_BRIDGED; /* The physdev module checks on this */
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_PRE_ROUTING,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/net/ipv4/netfilter/ipt_physdev.c	Thu Jul 24 00:01:09 2003
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ linux-2.4.21-ebt-brnf-2/include/linux/netfilter_ipv4/ipt_physdev.h	Thu Jul 24 01:58:23 2003
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.21/net/8021q/vlan_dev.c	Fri Jun 13 16:51:39 2003
++++ linux-2.4.21-ebt-brnf-2/net/8021q/vlan_dev.c	Thu Jul 24 00:01:09 2003
+@@ -149,7 +149,9 @@ int vlan_skb_recv(struct sk_buff *skb, s
+ 		printk(VLAN_DBG "%s: ERROR: No net_device for VID: %i on dev: %s [%i]\n",
+ 			__FUNCTION__, (unsigned int)(vid), dev->name, dev->ifindex);
+ #endif
++#if !(defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
+ 		kfree_skb(skb);
++#endif
+ 		return -1;
+ 	}
+ 
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.22.diff b/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.22.diff
new file mode 100644
index 0000000..773df50
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf-2_vs_2.4.22.diff
@@ -0,0 +1,5528 @@
+--- linux-2.4.22/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_private.h	2003-09-03 20:06:25.000000000 +0200
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.22/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/if_bridge.h	2003-09-03 20:06:59.000000000 +0200
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.22/net/core/dev.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/dev.c	2003-09-03 20:08:46.000000000 +0200
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.22/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_input.c	2003-09-03 20:10:57.000000000 +0200
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.22/net/bridge/br_forward.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_forward.c	2003-09-03 20:12:27.000000000 +0200
+@@ -30,18 +30,22 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +53,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.22/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br.c	2003-09-03 20:13:38.000000000 +0200
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.22/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/Makefile	2003-09-03 20:14:17.000000000 +0200
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.22/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge.h	2003-09-03 20:15:34.000000000 +0200
+@@ -6,6 +6,9 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +21,49 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.22/net/Makefile	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Makefile	2003-09-03 20:16:29.000000000 +0200
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.22/net/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Config.in	2003-09-03 20:17:08.000000000 +0200
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Makefile	2003-09-03 21:10:21.000000000 +0200
+@@ -0,0 +1,31 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Config.in	2003-09-03 21:10:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.22/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter.h	2003-09-03 20:20:40.000000000 +0200
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.22/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4.h	2003-09-03 20:25:14.000000000 +0200
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.22/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/skbuff.h	2003-09-03 20:26:39.000000000 +0200
+@@ -92,6 +92,17 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +219,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1183,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.22/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/netfilter.c	2003-09-03 21:39:40.000000000 +0200
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.22/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/skbuff.c	2003-09-03 20:31:51.000000000 +0200
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.22/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-09-03 20:36:13.000000000 +0200
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.22/net/ipv4/ip_output.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/ip_output.c	2003-09-03 20:36:57.000000000 +0200
+@@ -882,6 +882,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.22/net/ipv4/netfilter/ipt_LOG.c	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-09-03 20:37:40.000000000 +0200
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.22/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Makefile	2003-09-03 20:38:15.000000000 +0200
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.22/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Config.in	2003-09-03 21:40:55.000000000 +0200
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br_netfilter.c	2003-09-03 21:14:20.000000000 +0200
+@@ -0,0 +1,636 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bdschuym@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_BRIDGED; /* The physdev module checks on this */
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_PRE_ROUTING,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-09-03 21:15:28.000000000 +0200
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-09-03 21:15:59.000000000 +0200
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.22.diff b/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.22.diff
new file mode 100644
index 0000000..5e04e3a
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.22.diff
@@ -0,0 +1,6102 @@
+--- linux-2.4.22/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_private.h	2003-11-02 14:52:08.000000000 +0100
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.22/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/if_bridge.h	2003-11-02 14:25:37.000000000 +0100
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.22/net/core/dev.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/dev.c	2003-11-02 14:02:21.000000000 +0100
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.22/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_input.c	2003-11-02 14:02:21.000000000 +0100
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.22/net/bridge/br_forward.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_forward.c	2003-11-02 14:06:03.000000000 +0100
+@@ -30,18 +30,21 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	nf_bridge_maybe_copy_header(skb);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +52,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +67,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.22/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br.c	2003-11-02 14:02:21.000000000 +0100
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.22/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/Makefile	2003-11-02 14:02:21.000000000 +0100
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.22/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge.h	2003-11-02 14:52:11.000000000 +0100
+@@ -6,6 +6,12 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++#include <linux/if_ether.h>
++#endif
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +24,80 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++#define BRNF_NF_BRIDGE_PREROUTING	0x10
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		(*nf_bridge)->netoutdev = NULL;
++#endif
++	}
++
++	return *nf_bridge;
++}
++
++/* Only used in br_forward.c */
++static inline
++void nf_bridge_maybe_copy_header(struct sk_buff *skb)
++{
++	if (skb->nf_bridge) {
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			memcpy(skb->data - 18, skb->nf_bridge->hh, 18);
++			skb_push(skb, 4);
++		} else
++#endif
++			memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++	}
++}
++
++static inline
++void nf_bridge_save_header(struct sk_buff *skb)
++{
++        int header_size = 16;
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	if (skb->protocol == __constant_htons(ETH_P_8021Q))
++		header_size = 18;
++#endif
++	memcpy(skb->nf_bridge->hh, skb->data - header_size, header_size);
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.22/net/Makefile	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Makefile	2003-11-02 14:02:21.000000000 +0100
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.22/net/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Config.in	2003-11-02 14:02:21.000000000 +0100
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Makefile	2003-11-02 14:48:14.000000000 +0100
+@@ -0,0 +1,33 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Config.in	2003-11-02 14:50:51.000000000 +0100
+@@ -0,0 +1,22 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: among filter support' CONFIG_BRIDGE_EBT_AMONG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: limit filter support' CONFIG_BRIDGE_EBT_LIMIT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_among.c	2003-11-02 14:42:39.000000000 +0100
+@@ -0,0 +1,223 @@
++/*
++ *  ebt_among
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_among.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
++				     const char *mac, uint32_t ip)
++{
++	/* You may be puzzled as to how this code works.
++	 * Some tricks were used, refer to 
++	 * 	include/linux/netfilter_bridge/ebt_among.h
++	 * as there you can find a solution of this mystery.
++	 */
++	const struct ebt_mac_wormhash_tuple *p;
++	int start, limit, i;
++	uint32_t cmp[2] = { 0, 0 };
++	int key = (const unsigned char) mac[5];
++
++	memcpy(((char *) cmp) + 2, mac, 6);
++	start = wh->table[key];
++	limit = wh->table[key + 1];
++	if (ip) {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0 || p->ip == ip) {
++					return 1;
++				}
++			}
++		}
++	} else {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0) {
++					return 1;
++				}
++			}
++		}
++	}
++	return 0;
++}
++
++static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
++					    *wh)
++{
++	int i;
++
++	for (i = 0; i < 256; i++) {
++		if (wh->table[i] > wh->table[i + 1])
++			return -0x100 - i;
++		if (wh->table[i] < 0)
++			return -0x200 - i;
++		if (wh->table[i] > wh->poolsize)
++			return -0x300 - i;
++	}
++	if (wh->table[256] > wh->poolsize)
++		return -0xc00;
++	return 0;
++}
++
++static int get_ip_dst(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->daddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       (2 * (((*skb).nh.arph)->ar_hln)) +
++		       (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int get_ip_src(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->saddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int ebt_filter_among(const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out, const void *data,
++			    unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	const char *dmac, *smac;
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	uint32_t dip = 0, sip = 0;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++
++	if (wh_src) {
++		smac = skb->mac.ethernet->h_source;
++		if (get_ip_src(skb, &sip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (wh_dst) {
++		dmac = skb->mac.ethernet->h_dest;
++		if (get_ip_dst(skb, &dip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_among_check(const char *tablename, unsigned int hookmask,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	int expected_length = sizeof(struct ebt_among_info);
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	int err;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++	expected_length += ebt_mac_wormhash_size(wh_dst);
++	expected_length += ebt_mac_wormhash_size(wh_src);
++
++	if (datalen != EBT_ALIGN(expected_length)) {
++		printk(KERN_WARNING
++		       "ebtables: among: wrong size: %d"
++		       "against expected %d, rounded to %d\n",
++		       datalen, expected_length,
++		       EBT_ALIGN(expected_length));
++		return -EINVAL;
++	}
++	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
++		printk(KERN_WARNING
++		       "ebtables: among: dst integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
++		printk(KERN_WARNING
++		       "ebtables: among: src integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_among = {
++	{NULL, NULL}, 
++	EBT_AMONG_MATCH, 
++	ebt_filter_among, 
++	ebt_among_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_among);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_among);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_limit.c	2003-11-02 14:39:15.000000000 +0100
+@@ -0,0 +1,101 @@
++/*
++ *  ebt_limit
++ *
++ *	Authors:
++ *	Tom Marshall <tommy@home.tig-grr.com>
++ *
++ *	Mostly copied from netfilter's ipt_limit.c, see that file for explanation
++ *
++ *  September, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_limit.h>
++#include <linux/module.h>
++
++#include <linux/netdevice.h>
++#include <linux/spinlock.h>
++
++static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
++
++#define CREDITS_PER_JIFFY 128
++
++static int ebt_limit_match(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++	unsigned long now = jiffies;
++
++	spin_lock_bh(&limit_lock);
++	info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
++	if (info->credit > info->credit_cap)
++		info->credit = info->credit_cap;
++
++	if (info->credit >= info->cost) {
++		/* We're not limited. */
++		info->credit -= info->cost;
++		spin_unlock_bh(&limit_lock);
++		return EBT_MATCH;
++	}
++
++	spin_unlock_bh(&limit_lock);
++	return EBT_NOMATCH;
++}
++
++/* Precision saver. */
++static u_int32_t
++user2credits(u_int32_t user)
++{
++	/* If multiplying would overflow... */
++	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
++		/* Divide first. */
++		return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
++
++	return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
++}
++
++static int ebt_limit_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
++		return -EINVAL;
++
++	/* Check for overflow. */
++	if (info->burst == 0
++	    || user2credits(info->avg * info->burst) < user2credits(info->avg)) {
++		printk("Overflow in ebt_limit: %u/%u\n",
++			info->avg, info->burst);
++		return -EINVAL;
++	}
++
++	/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
++	info->prev = jiffies;
++	info->credit = user2credits(info->avg * info->burst);
++	info->credit_cap = user2credits(info->avg * info->burst);
++	info->cost = user2credits(info->avg);
++	return 0;
++}
++
++static struct ebt_match ebt_limit_reg =
++{
++	{NULL, NULL}, EBT_LIMIT_MATCH, ebt_limit_match, ebt_limit_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&ebt_limit_reg);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&ebt_limit_reg);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-11-02 15:10:12.000000000 +0100
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_among.h	2003-11-02 14:43:27.000000000 +0100
+@@ -0,0 +1,65 @@
++#ifndef __LINUX_BRIDGE_EBT_AMONG_H
++#define __LINUX_BRIDGE_EBT_AMONG_H
++
++#define EBT_AMONG_DST 0x01
++#define EBT_AMONG_SRC 0x02
++
++/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
++ * 
++ * Write-once-read-many hash table, used for checking if a given
++ * MAC address belongs to a set or not and possibly for checking
++ * if it is related with a given IPv4 address.
++ *
++ * The hash value of an address is its last byte.
++ * 
++ * In real-world ethernet addresses, values of the last byte are
++ * evenly distributed and there is no need to consider other bytes.
++ * It would only slow the routines down.
++ *
++ * For MAC address comparison speedup reasons, we introduce a trick.
++ * MAC address is mapped onto an array of two 32-bit integers.
++ * This pair of integers is compared with MAC addresses in the
++ * hash table, which are stored also in form of pairs of integers
++ * (in `cmp' array). This is quick as it requires only two elementary
++ * number comparisons in worst case. Further, we take advantage of
++ * fact that entropy of 3 last bytes of address is larger than entropy
++ * of 3 first bytes. So first we compare 4 last bytes of addresses and
++ * if they are the same we compare 2 first.
++ *
++ * Yes, it is a memory overhead, but in 2003 AD, who cares?
++ */
++
++struct ebt_mac_wormhash_tuple
++{
++	uint32_t cmp[2];
++	uint32_t ip;
++};
++
++struct ebt_mac_wormhash
++{
++	int table[257];
++	int poolsize;
++	struct ebt_mac_wormhash_tuple pool[0];
++};
++
++#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
++		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
++
++struct ebt_among_info
++{
++	int wh_dst_ofs;
++	int wh_src_ofs;
++	int bitmask;
++};
++
++#define EBT_AMONG_DST_NEG 0x1
++#define EBT_AMONG_SRC_NEG 0x2
++
++#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
++#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
++
++#define EBT_AMONG_MATCH "among"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_limit.h	2003-11-02 14:40:34.000000000 +0100
+@@ -0,0 +1,23 @@
++#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
++#define __LINUX_BRIDGE_EBT_LIMIT_H
++
++#define EBT_LIMIT_MATCH "limit"
++
++/* timings are in milliseconds. */
++#define EBT_LIMIT_SCALE 10000
++
++/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
++   seconds, or one every 59 hours. */
++
++struct ebt_limit_info
++{
++	u_int32_t avg;    /* Average secs between packets * scale */
++	u_int32_t burst;  /* Period multiplier for upper limit. */
++
++	/* Used internally by the kernel */
++	unsigned long prev;
++	u_int32_t credit;
++	u_int32_t credit_cap, cost;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.22/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter.h	2003-11-02 14:25:45.000000000 +0100
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.22/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4.h	2003-11-02 14:25:45.000000000 +0100
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.22/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/skbuff.h	2003-11-02 14:21:06.000000000 +0100
+@@ -92,6 +92,20 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	struct net_device *netoutdev;
++#endif
++	unsigned int mask;
++	unsigned long hh[32 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +222,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1186,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.22/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/netfilter.c	2003-11-02 14:02:21.000000000 +0100
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.22/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/skbuff.c	2003-11-02 14:13:49.000000000 +0100
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+@@ -726,9 +742,9 @@ struct sk_buff *skb_copy_expand(const st
+ 	/* Set the tail pointer and length */
+ 	skb_put(n,skb->len);
+ 
+-	/* Copy the data only. */
+-	if (skb_copy_bits(skb, 0, n->data, skb->len))
+-		BUG();
++       /* Copy the linear data and header. */
++       if (skb_copy_bits(skb, -newheadroom, n->head, newheadroom + skb->len))
++                BUG();
+ 
+ 	copy_skb_header(n, skb);
+ 	return n;
+--- linux-2.4.22/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-11-02 14:02:21.000000000 +0100
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.22/net/ipv4/ip_output.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/ip_output.c	2003-11-02 14:02:21.000000000 +0100
+@@ -882,6 +882,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.22/net/ipv4/netfilter/ipt_LOG.c	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-11-02 14:02:21.000000000 +0100
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.22/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Makefile	2003-11-02 14:02:21.000000000 +0100
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.22/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Config.in	2003-11-02 14:02:21.000000000 +0100
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br_netfilter.c	2003-11-02 15:10:06.000000000 +0100
+@@ -0,0 +1,720 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer (maintainer)	<bdschuym@pandora.be>
++ *
++ *	Changes:
++ *	Apr 29 2003: physdev module support (bdschuym)
++ *	Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
++ *	Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
++ *	             (bdschuym)
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
++	hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++	skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				if (skb->protocol ==
++				    __constant_htons(ETH_P_8021Q)) {
++					skb_push(skb, VLAN_HLEN);
++					skb->nh.raw -= VLAN_HLEN;
++				}
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++			skb->pkt_type = PACKET_HOST;
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)
++					  ((*pskb)->mac.ethernet);
++
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++			goto out;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	} else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++	struct net_device *in;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) {
++		in = nf_bridge->physindev;
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++	} else {
++		in = *((struct net_device **)(skb->cb));
++	}
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
++			skb->dev, br_forward_finish, 1);
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  For IP, we pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	/* The physdev module checks on this */
++	nf_bridge->mask |= BRNF_BRIDGED;
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in),
++		bridge_parent(out), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_push(skb, VLAN_HLEN);
++			skb->nh.raw -= VLAN_HLEN;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		struct net_device *realoutdev = bridge_parent(skb->dev);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* iptables should match -o br0.x */
++		if (nf_bridge->netoutdev)
++			realoutdev = nf_bridge->netoutdev;
++#endif
++		okfn = br_nf_local_out_finish;
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_pull(skb, VLAN_HLEN);
++			(*pskb)->nh.raw += VLAN_HLEN;
++		}
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++	struct net_device *realoutdev = bridge_parent(skb->dev);
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk(" head:%p, raw:%p\n", skb->head, skb->mac.raw);
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++
++	nf_bridge_save_header(skb);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	if (nf_bridge->netoutdev)
++		realoutdev = nf_bridge->netoutdev;
++#endif
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		realoutdev, br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if ((*pskb)->nf_bridge &&
++	    !((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if ((out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit)
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	    || ((out->priv_flags & IFF_802_1Q_VLAN) &&
++	    VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit)
++#endif
++	    ) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* the iptables outdev is br0.x, not br0 */
++		if (out->priv_flags & IFF_802_1Q_VLAN)
++			nf_bridge->netoutdev = (struct net_device *)out;
++#endif
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing, 
++	  .pf = PF_BRIDGE, 
++	  .hooknum = NF_BR_PRE_ROUTING, 
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-11-02 14:02:21.000000000 +0100
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-11-02 15:10:36.000000000 +0100
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.22/net/8021q/vlan_dev.c	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/8021q/vlan_dev.c	2003-11-02 14:06:43.000000000 +0100
+@@ -503,6 +503,10 @@ int vlan_dev_hard_start_xmit(struct sk_b
+ 	stats->tx_packets++; /* for statics only */
+ 	stats->tx_bytes += skb->len;
+ 
++	skb->protocol = __constant_htons(ETH_P_8021Q);
++	skb->mac.raw -= VLAN_HLEN;
++	skb->nh.raw -= VLAN_HLEN;
++
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.23.diff b/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.23.diff
new file mode 100644
index 0000000..e20e48c
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf-3_vs_2.4.23.diff
@@ -0,0 +1,6102 @@
+--- linux-2.4.23/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/bridge/br_private.h	2003-12-15 21:29:45.000000000 +0100
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.23/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/if_bridge.h	2003-12-15 21:29:45.000000000 +0100
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.23/net/core/dev.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/core/dev.c	2003-12-15 21:29:45.000000000 +0100
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.23/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/bridge/br_input.c	2003-12-15 21:29:45.000000000 +0100
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.23/net/bridge/br_forward.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/br_forward.c	2003-12-15 21:29:45.000000000 +0100
+@@ -30,18 +30,21 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	nf_bridge_maybe_copy_header(skb);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +52,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -62,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->ip_summed = CHECKSUM_NONE;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.23/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/br.c	2003-12-15 21:29:45.000000000 +0100
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.23/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/Makefile	2003-12-15 21:29:45.000000000 +0100
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.23/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge.h	2003-12-15 21:29:45.000000000 +0100
+@@ -6,6 +6,12 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++#include <linux/if_ether.h>
++#endif
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +24,80 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++#define BRNF_NF_BRIDGE_PREROUTING	0x10
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		(*nf_bridge)->netoutdev = NULL;
++#endif
++	}
++
++	return *nf_bridge;
++}
++
++/* Only used in br_forward.c */
++static inline
++void nf_bridge_maybe_copy_header(struct sk_buff *skb)
++{
++	if (skb->nf_bridge) {
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			memcpy(skb->data - 18, skb->nf_bridge->hh, 18);
++			skb_push(skb, 4);
++		} else
++#endif
++			memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++	}
++}
++
++static inline
++void nf_bridge_save_header(struct sk_buff *skb)
++{
++        int header_size = 16;
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	if (skb->protocol == __constant_htons(ETH_P_8021Q))
++		header_size = 18;
++#endif
++	memcpy(skb->nf_bridge->hh, skb->data - header_size, header_size);
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.23/net/Makefile	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/Makefile	2003-12-15 21:30:50.000000000 +0100
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core sctp
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -27,6 +28,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.23/net/Config.in	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/Config.in	2003-12-15 21:29:45.000000000 +0100
+@@ -68,6 +68,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/Makefile	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,33 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/Config.in	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,22 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: among filter support' CONFIG_BRIDGE_EBT_AMONG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: limit filter support' CONFIG_BRIDGE_EBT_LIMIT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_among.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,223 @@
++/*
++ *  ebt_among
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_among.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
++				     const char *mac, uint32_t ip)
++{
++	/* You may be puzzled as to how this code works.
++	 * Some tricks were used, refer to 
++	 * 	include/linux/netfilter_bridge/ebt_among.h
++	 * as there you can find a solution of this mystery.
++	 */
++	const struct ebt_mac_wormhash_tuple *p;
++	int start, limit, i;
++	uint32_t cmp[2] = { 0, 0 };
++	int key = (const unsigned char) mac[5];
++
++	memcpy(((char *) cmp) + 2, mac, 6);
++	start = wh->table[key];
++	limit = wh->table[key + 1];
++	if (ip) {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0 || p->ip == ip) {
++					return 1;
++				}
++			}
++		}
++	} else {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0) {
++					return 1;
++				}
++			}
++		}
++	}
++	return 0;
++}
++
++static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
++					    *wh)
++{
++	int i;
++
++	for (i = 0; i < 256; i++) {
++		if (wh->table[i] > wh->table[i + 1])
++			return -0x100 - i;
++		if (wh->table[i] < 0)
++			return -0x200 - i;
++		if (wh->table[i] > wh->poolsize)
++			return -0x300 - i;
++	}
++	if (wh->table[256] > wh->poolsize)
++		return -0xc00;
++	return 0;
++}
++
++static int get_ip_dst(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->daddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       (2 * (((*skb).nh.arph)->ar_hln)) +
++		       (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int get_ip_src(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->saddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int ebt_filter_among(const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out, const void *data,
++			    unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	const char *dmac, *smac;
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	uint32_t dip = 0, sip = 0;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++
++	if (wh_src) {
++		smac = skb->mac.ethernet->h_source;
++		if (get_ip_src(skb, &sip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (wh_dst) {
++		dmac = skb->mac.ethernet->h_dest;
++		if (get_ip_dst(skb, &dip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_among_check(const char *tablename, unsigned int hookmask,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	int expected_length = sizeof(struct ebt_among_info);
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	int err;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++	expected_length += ebt_mac_wormhash_size(wh_dst);
++	expected_length += ebt_mac_wormhash_size(wh_src);
++
++	if (datalen != EBT_ALIGN(expected_length)) {
++		printk(KERN_WARNING
++		       "ebtables: among: wrong size: %d"
++		       "against expected %d, rounded to %d\n",
++		       datalen, expected_length,
++		       EBT_ALIGN(expected_length));
++		return -EINVAL;
++	}
++	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
++		printk(KERN_WARNING
++		       "ebtables: among: dst integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
++		printk(KERN_WARNING
++		       "ebtables: among: src integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_among = {
++	{NULL, NULL}, 
++	EBT_AMONG_MATCH, 
++	ebt_filter_among, 
++	ebt_among_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_among);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_among);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_limit.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,101 @@
++/*
++ *  ebt_limit
++ *
++ *	Authors:
++ *	Tom Marshall <tommy@home.tig-grr.com>
++ *
++ *	Mostly copied from netfilter's ipt_limit.c, see that file for explanation
++ *
++ *  September, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_limit.h>
++#include <linux/module.h>
++
++#include <linux/netdevice.h>
++#include <linux/spinlock.h>
++
++static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
++
++#define CREDITS_PER_JIFFY 128
++
++static int ebt_limit_match(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++	unsigned long now = jiffies;
++
++	spin_lock_bh(&limit_lock);
++	info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
++	if (info->credit > info->credit_cap)
++		info->credit = info->credit_cap;
++
++	if (info->credit >= info->cost) {
++		/* We're not limited. */
++		info->credit -= info->cost;
++		spin_unlock_bh(&limit_lock);
++		return EBT_MATCH;
++	}
++
++	spin_unlock_bh(&limit_lock);
++	return EBT_NOMATCH;
++}
++
++/* Precision saver. */
++static u_int32_t
++user2credits(u_int32_t user)
++{
++	/* If multiplying would overflow... */
++	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
++		/* Divide first. */
++		return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
++
++	return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
++}
++
++static int ebt_limit_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
++		return -EINVAL;
++
++	/* Check for overflow. */
++	if (info->burst == 0
++	    || user2credits(info->avg * info->burst) < user2credits(info->avg)) {
++		printk("Overflow in ebt_limit: %u/%u\n",
++			info->avg, info->burst);
++		return -EINVAL;
++	}
++
++	/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
++	info->prev = jiffies;
++	info->credit = user2credits(info->avg * info->burst);
++	info->credit_cap = user2credits(info->avg * info->burst);
++	info->cost = user2credits(info->avg);
++	return 0;
++}
++
++static struct ebt_match ebt_limit_reg =
++{
++	{NULL, NULL}, EBT_LIMIT_MATCH, ebt_limit_match, ebt_limit_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&ebt_limit_reg);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&ebt_limit_reg);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_among.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,65 @@
++#ifndef __LINUX_BRIDGE_EBT_AMONG_H
++#define __LINUX_BRIDGE_EBT_AMONG_H
++
++#define EBT_AMONG_DST 0x01
++#define EBT_AMONG_SRC 0x02
++
++/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
++ * 
++ * Write-once-read-many hash table, used for checking if a given
++ * MAC address belongs to a set or not and possibly for checking
++ * if it is related with a given IPv4 address.
++ *
++ * The hash value of an address is its last byte.
++ * 
++ * In real-world ethernet addresses, values of the last byte are
++ * evenly distributed and there is no need to consider other bytes.
++ * It would only slow the routines down.
++ *
++ * For MAC address comparison speedup reasons, we introduce a trick.
++ * MAC address is mapped onto an array of two 32-bit integers.
++ * This pair of integers is compared with MAC addresses in the
++ * hash table, which are stored also in form of pairs of integers
++ * (in `cmp' array). This is quick as it requires only two elementary
++ * number comparisons in worst case. Further, we take advantage of
++ * fact that entropy of 3 last bytes of address is larger than entropy
++ * of 3 first bytes. So first we compare 4 last bytes of addresses and
++ * if they are the same we compare 2 first.
++ *
++ * Yes, it is a memory overhead, but in 2003 AD, who cares?
++ */
++
++struct ebt_mac_wormhash_tuple
++{
++	uint32_t cmp[2];
++	uint32_t ip;
++};
++
++struct ebt_mac_wormhash
++{
++	int table[257];
++	int poolsize;
++	struct ebt_mac_wormhash_tuple pool[0];
++};
++
++#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
++		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
++
++struct ebt_among_info
++{
++	int wh_dst_ofs;
++	int wh_src_ofs;
++	int bitmask;
++};
++
++#define EBT_AMONG_DST_NEG 0x1
++#define EBT_AMONG_SRC_NEG 0x2
++
++#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
++#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
++
++#define EBT_AMONG_MATCH "among"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_limit.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,23 @@
++#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
++#define __LINUX_BRIDGE_EBT_LIMIT_H
++
++#define EBT_LIMIT_MATCH "limit"
++
++/* timings are in milliseconds. */
++#define EBT_LIMIT_SCALE 10000
++
++/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
++   seconds, or one every 59 hours. */
++
++struct ebt_limit_info
++{
++	u_int32_t avg;    /* Average secs between packets * scale */
++	u_int32_t burst;  /* Period multiplier for upper limit. */
++
++	/* Used internally by the kernel */
++	unsigned long prev;
++	u_int32_t credit;
++	u_int32_t credit_cap, cost;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.23/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter.h	2003-12-15 21:29:45.000000000 +0100
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.23/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_ipv4.h	2003-12-15 21:29:45.000000000 +0100
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.23/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/include/linux/skbuff.h	2003-12-15 21:29:45.000000000 +0100
+@@ -92,6 +92,20 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	struct net_device *netoutdev;
++#endif
++	unsigned int mask;
++	unsigned long hh[32 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +222,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1186,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.23/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/core/netfilter.c	2003-12-15 21:29:45.000000000 +0100
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.23/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/core/skbuff.c	2003-12-15 21:29:45.000000000 +0100
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+@@ -726,9 +742,9 @@ struct sk_buff *skb_copy_expand(const st
+ 	/* Set the tail pointer and length */
+ 	skb_put(n,skb->len);
+ 
+-	/* Copy the data only. */
+-	if (skb_copy_bits(skb, 0, n->data, skb->len))
+-		BUG();
++       /* Copy the linear data and header. */
++       if (skb_copy_bits(skb, -newheadroom, n->head, newheadroom + skb->len))
++                BUG();
+ 
+ 	copy_skb_header(n, skb);
+ 	return n;
+--- linux-2.4.23/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-12-15 21:29:45.000000000 +0100
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.23/net/ipv4/ip_output.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/ipv4/ip_output.c	2003-12-15 21:29:45.000000000 +0100
+@@ -883,6 +883,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.23/net/ipv4/netfilter/ipt_LOG.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-12-15 21:29:45.000000000 +0100
+@@ -316,6 +316,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.23/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/ipv4/netfilter/Makefile	2003-12-15 21:29:45.000000000 +0100
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.23/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.23-ebt-brnf/net/ipv4/netfilter/Config.in	2003-12-15 21:29:45.000000000 +0100
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/bridge/br_netfilter.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,720 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer (maintainer)	<bdschuym@pandora.be>
++ *
++ *	Changes:
++ *	Apr 29 2003: physdev module support (bdschuym)
++ *	Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
++ *	Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
++ *	             (bdschuym)
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
++	hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++	skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				if (skb->protocol ==
++				    __constant_htons(ETH_P_8021Q)) {
++					skb_push(skb, VLAN_HLEN);
++					skb->nh.raw -= VLAN_HLEN;
++				}
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++			skb->pkt_type = PACKET_HOST;
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)
++					  ((*pskb)->mac.ethernet);
++
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++			goto out;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	} else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++	struct net_device *in;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) {
++		in = nf_bridge->physindev;
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++	} else {
++		in = *((struct net_device **)(skb->cb));
++	}
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
++			skb->dev, br_forward_finish, 1);
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  For IP, we pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	/* The physdev module checks on this */
++	nf_bridge->mask |= BRNF_BRIDGED;
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in),
++		bridge_parent(out), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_push(skb, VLAN_HLEN);
++			skb->nh.raw -= VLAN_HLEN;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		struct net_device *realoutdev = bridge_parent(skb->dev);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* iptables should match -o br0.x */
++		if (nf_bridge->netoutdev)
++			realoutdev = nf_bridge->netoutdev;
++#endif
++		okfn = br_nf_local_out_finish;
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_pull(skb, VLAN_HLEN);
++			(*pskb)->nh.raw += VLAN_HLEN;
++		}
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++	struct net_device *realoutdev = bridge_parent(skb->dev);
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk(" head:%p, raw:%p\n", skb->head, skb->mac.raw);
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++
++	nf_bridge_save_header(skb);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	if (nf_bridge->netoutdev)
++		realoutdev = nf_bridge->netoutdev;
++#endif
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		realoutdev, br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if ((*pskb)->nf_bridge &&
++	    !((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if ((out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit)
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	    || ((out->priv_flags & IFF_802_1Q_VLAN) &&
++	    VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit)
++#endif
++	    ) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* the iptables outdev is br0.x, not br0 */
++		if (out->priv_flags & IFF_802_1Q_VLAN)
++			nf_bridge->netoutdev = (struct net_device *)out;
++#endif
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing, 
++	  .pf = PF_BRIDGE, 
++	  .hooknum = NF_BR_PRE_ROUTING, 
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.23-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-12-15 21:29:45.000000000 +0100
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.23/net/8021q/vlan_dev.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.23-ebt-brnf/net/8021q/vlan_dev.c	2003-12-15 21:29:45.000000000 +0100
+@@ -507,6 +507,10 @@ int vlan_dev_hard_start_xmit(struct sk_b
+ 	stats->tx_packets++; /* for statics only */
+ 	stats->tx_bytes += skb->len;
+ 
++	skb->protocol = __constant_htons(ETH_P_8021Q);
++	skb->mac.raw -= VLAN_HLEN;
++	skb->nh.raw -= VLAN_HLEN;
++
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf-4_vs_2.4.24.diff b/kernel/patches/ebtables-brnf/ebtables-brnf-4_vs_2.4.24.diff
new file mode 100644
index 0000000..9b831f1
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf-4_vs_2.4.24.diff
@@ -0,0 +1,6291 @@
+--- linux-2.4.24/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/bridge/br_private.h	2004-01-06 21:15:07.000000000 +0100
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.24/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/if_bridge.h	2004-01-06 21:10:35.000000000 +0100
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.24/net/core/dev.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/core/dev.c	2004-01-06 20:37:32.000000000 +0100
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.24/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/bridge/br_input.c	2004-01-06 20:37:32.000000000 +0100
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.24/net/bridge/br_forward.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/br_forward.c	2004-01-06 20:37:32.000000000 +0100
+@@ -30,18 +30,21 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	nf_bridge_maybe_copy_header(skb);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +52,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -62,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->ip_summed = CHECKSUM_NONE;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.24/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/br.c	2004-01-06 20:37:32.000000000 +0100
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.24/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/Makefile	2004-01-06 20:37:32.000000000 +0100
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.24/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge.h	2004-01-06 21:15:09.000000000 +0100
+@@ -6,6 +6,10 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#include <linux/if_ether.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +22,76 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++#define BRNF_NF_BRIDGE_PREROUTING	0x10
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		(*nf_bridge)->netoutdev = NULL;
++#endif
++	}
++
++	return *nf_bridge;
++}
++
++/* Only used in br_forward.c */
++static inline
++void nf_bridge_maybe_copy_header(struct sk_buff *skb)
++{
++	if (skb->nf_bridge) {
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			memcpy(skb->data - 18, skb->nf_bridge->data, 18);
++			skb_push(skb, 4);
++		} else
++			memcpy(skb->data - 16, skb->nf_bridge->data, 16);
++	}
++}
++
++static inline
++void nf_bridge_save_header(struct sk_buff *skb)
++{
++        int header_size = 16;
++
++	if (skb->protocol == __constant_htons(ETH_P_8021Q))
++		header_size = 18;
++	memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.24/net/Makefile	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/Makefile	2004-01-06 20:37:32.000000000 +0100
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core sctp
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -27,6 +28,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.24/net/Config.in	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/Config.in	2004-01-06 20:37:32.000000000 +0100
+@@ -68,6 +68,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/Makefile	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,33 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/Config.in	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,22 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: among filter support' CONFIG_BRIDGE_EBT_AMONG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: limit filter support' CONFIG_BRIDGE_EBT_LIMIT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_among.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,223 @@
++/*
++ *  ebt_among
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_among.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++static int ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,
++				     const char *mac, uint32_t ip)
++{
++	/* You may be puzzled as to how this code works.
++	 * Some tricks were used, refer to 
++	 * 	include/linux/netfilter_bridge/ebt_among.h
++	 * as there you can find a solution of this mystery.
++	 */
++	const struct ebt_mac_wormhash_tuple *p;
++	int start, limit, i;
++	uint32_t cmp[2] = { 0, 0 };
++	int key = (const unsigned char) mac[5];
++
++	memcpy(((char *) cmp) + 2, mac, 6);
++	start = wh->table[key];
++	limit = wh->table[key + 1];
++	if (ip) {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0 || p->ip == ip) {
++					return 1;
++				}
++			}
++		}
++	} else {
++		for (i = start; i < limit; i++) {
++			p = &wh->pool[i];
++			if (cmp[1] == p->cmp[1] && cmp[0] == p->cmp[0]) {
++				if (p->ip == 0) {
++					return 1;
++				}
++			}
++		}
++	}
++	return 0;
++}
++
++static int ebt_mac_wormhash_check_integrity(const struct ebt_mac_wormhash
++					    *wh)
++{
++	int i;
++
++	for (i = 0; i < 256; i++) {
++		if (wh->table[i] > wh->table[i + 1])
++			return -0x100 - i;
++		if (wh->table[i] < 0)
++			return -0x200 - i;
++		if (wh->table[i] > wh->poolsize)
++			return -0x300 - i;
++	}
++	if (wh->table[256] > wh->poolsize)
++		return -0xc00;
++	return 0;
++}
++
++static int get_ip_dst(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->daddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       (2 * (((*skb).nh.arph)->ar_hln)) +
++		       (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int get_ip_src(const struct sk_buff *skb, uint32_t * addr)
++{
++	if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP))
++		*addr = skb->nh.iph->saddr;
++	else if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) {
++		uint32_t arp_len = sizeof(struct arphdr) +
++		    (2 * (((*skb).nh.arph)->ar_hln)) +
++		    (2 * (((*skb).nh.arph)->ar_pln));
++
++		/* Make sure the packet is long enough. */
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return -1;
++		/* IPv4 addresses are always 4 bytes. */
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return -1;
++
++		memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) +
++		       ((((*skb).nh.arph)->ar_hln)), sizeof(uint32_t));
++
++	}
++	return 0;
++}
++
++static int ebt_filter_among(const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out, const void *data,
++			    unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	const char *dmac, *smac;
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	uint32_t dip = 0, sip = 0;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++
++	if (wh_src) {
++		smac = skb->mac.ethernet->h_source;
++		if (get_ip_src(skb, &sip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_SRC_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_src, smac, sip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (wh_dst) {
++		dmac = skb->mac.ethernet->h_dest;
++		if (get_ip_dst(skb, &dip))
++			return EBT_NOMATCH;
++		if (!(info->bitmask & EBT_AMONG_DST_NEG)) {
++			/* we match only if it contains */
++			if (!ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		} else {
++			/* we match only if it DOES NOT contain */
++			if (ebt_mac_wormhash_contains(wh_dst, dmac, dip))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_among_check(const char *tablename, unsigned int hookmask,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_among_info *info = (struct ebt_among_info *) data;
++	int expected_length = sizeof(struct ebt_among_info);
++	const struct ebt_mac_wormhash *wh_dst, *wh_src;
++	int err;
++
++	wh_dst = ebt_among_wh_dst(info);
++	wh_src = ebt_among_wh_src(info);
++	expected_length += ebt_mac_wormhash_size(wh_dst);
++	expected_length += ebt_mac_wormhash_size(wh_src);
++
++	if (datalen != EBT_ALIGN(expected_length)) {
++		printk(KERN_WARNING
++		       "ebtables: among: wrong size: %d"
++		       "against expected %d, rounded to %d\n",
++		       datalen, expected_length,
++		       EBT_ALIGN(expected_length));
++		return -EINVAL;
++	}
++	if (wh_dst && (err = ebt_mac_wormhash_check_integrity(wh_dst))) {
++		printk(KERN_WARNING
++		       "ebtables: among: dst integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	if (wh_src && (err = ebt_mac_wormhash_check_integrity(wh_src))) {
++		printk(KERN_WARNING
++		       "ebtables: among: src integrity fail: %x\n", -err);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_among = {
++	{NULL, NULL}, 
++	EBT_AMONG_MATCH, 
++	ebt_filter_among, 
++	ebt_among_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_among);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_among);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_limit.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,101 @@
++/*
++ *  ebt_limit
++ *
++ *	Authors:
++ *	Tom Marshall <tommy@home.tig-grr.com>
++ *
++ *	Mostly copied from netfilter's ipt_limit.c, see that file for explanation
++ *
++ *  September, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_limit.h>
++#include <linux/module.h>
++
++#include <linux/netdevice.h>
++#include <linux/spinlock.h>
++
++static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
++
++#define CREDITS_PER_JIFFY 128
++
++static int ebt_limit_match(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++	unsigned long now = jiffies;
++
++	spin_lock_bh(&limit_lock);
++	info->credit += (now - xchg(&info->prev, now)) * CREDITS_PER_JIFFY;
++	if (info->credit > info->credit_cap)
++		info->credit = info->credit_cap;
++
++	if (info->credit >= info->cost) {
++		/* We're not limited. */
++		info->credit -= info->cost;
++		spin_unlock_bh(&limit_lock);
++		return EBT_MATCH;
++	}
++
++	spin_unlock_bh(&limit_lock);
++	return EBT_NOMATCH;
++}
++
++/* Precision saver. */
++static u_int32_t
++user2credits(u_int32_t user)
++{
++	/* If multiplying would overflow... */
++	if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
++		/* Divide first. */
++		return (user / EBT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
++
++	return (user * HZ * CREDITS_PER_JIFFY) / EBT_LIMIT_SCALE;
++}
++
++static int ebt_limit_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_limit_info *info = (struct ebt_limit_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_limit_info)))
++		return -EINVAL;
++
++	/* Check for overflow. */
++	if (info->burst == 0
++	    || user2credits(info->avg * info->burst) < user2credits(info->avg)) {
++		printk("Overflow in ebt_limit: %u/%u\n",
++			info->avg, info->burst);
++		return -EINVAL;
++	}
++
++	/* User avg in seconds * EBT_LIMIT_SCALE: convert to jiffies * 128. */
++	info->prev = jiffies;
++	info->credit = user2credits(info->avg * info->burst);
++	info->credit_cap = user2credits(info->avg * info->burst);
++	info->cost = user2credits(info->avg);
++	return 0;
++}
++
++static struct ebt_match ebt_limit_reg =
++{
++	{NULL, NULL}, EBT_LIMIT_MATCH, ebt_limit_match, ebt_limit_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&ebt_limit_reg);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&ebt_limit_reg);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_log.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/netfilter/ebtables.c	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2004-01-06 21:39:01.000000000 +0100
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_among.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,65 @@
++#ifndef __LINUX_BRIDGE_EBT_AMONG_H
++#define __LINUX_BRIDGE_EBT_AMONG_H
++
++#define EBT_AMONG_DST 0x01
++#define EBT_AMONG_SRC 0x02
++
++/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
++ * 
++ * Write-once-read-many hash table, used for checking if a given
++ * MAC address belongs to a set or not and possibly for checking
++ * if it is related with a given IPv4 address.
++ *
++ * The hash value of an address is its last byte.
++ * 
++ * In real-world ethernet addresses, values of the last byte are
++ * evenly distributed and there is no need to consider other bytes.
++ * It would only slow the routines down.
++ *
++ * For MAC address comparison speedup reasons, we introduce a trick.
++ * MAC address is mapped onto an array of two 32-bit integers.
++ * This pair of integers is compared with MAC addresses in the
++ * hash table, which are stored also in form of pairs of integers
++ * (in `cmp' array). This is quick as it requires only two elementary
++ * number comparisons in worst case. Further, we take advantage of
++ * fact that entropy of 3 last bytes of address is larger than entropy
++ * of 3 first bytes. So first we compare 4 last bytes of addresses and
++ * if they are the same we compare 2 first.
++ *
++ * Yes, it is a memory overhead, but in 2003 AD, who cares?
++ */
++
++struct ebt_mac_wormhash_tuple
++{
++	uint32_t cmp[2];
++	uint32_t ip;
++};
++
++struct ebt_mac_wormhash
++{
++	int table[257];
++	int poolsize;
++	struct ebt_mac_wormhash_tuple pool[0];
++};
++
++#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
++		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
++
++struct ebt_among_info
++{
++	int wh_dst_ofs;
++	int wh_src_ofs;
++	int bitmask;
++};
++
++#define EBT_AMONG_DST_NEG 0x1
++#define EBT_AMONG_SRC_NEG 0x2
++
++#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
++#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
++	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
++
++#define EBT_AMONG_MATCH "among"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_limit.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,23 @@
++#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
++#define __LINUX_BRIDGE_EBT_LIMIT_H
++
++#define EBT_LIMIT_MATCH "limit"
++
++/* timings are in milliseconds. */
++#define EBT_LIMIT_SCALE 10000
++
++/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
++   seconds, or one every 59 hours. */
++
++struct ebt_limit_info
++{
++	u_int32_t avg;    /* Average secs between packets * scale */
++	u_int32_t burst;  /* Period multiplier for upper limit. */
++
++	/* Used internally by the kernel */
++	unsigned long prev;
++	u_int32_t credit;
++	u_int32_t credit_cap, cost;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2004-01-06 20:37:32.000000000 +0100
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.24/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter.h	2004-01-06 21:10:43.000000000 +0100
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.24/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_ipv4.h	2004-01-06 21:10:43.000000000 +0100
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.24/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/include/linux/skbuff.h	2004-01-06 21:06:13.000000000 +0100
+@@ -92,6 +92,20 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	struct net_device *netoutdev;
++#endif
++	unsigned int mask;
++	unsigned long data[32 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +222,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1186,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.24/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/core/netfilter.c	2004-01-06 20:37:33.000000000 +0100
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.24/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/core/skbuff.c	2004-01-06 20:37:33.000000000 +0100
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+@@ -726,9 +742,9 @@ struct sk_buff *skb_copy_expand(const st
+ 	/* Set the tail pointer and length */
+ 	skb_put(n,skb->len);
+ 
+-	/* Copy the data only. */
+-	if (skb_copy_bits(skb, 0, n->data, skb->len))
+-		BUG();
++       /* Copy the linear data and header. */
++       if (skb_copy_bits(skb, -newheadroom, n->head, newheadroom + skb->len))
++                BUG();
+ 
+ 	copy_skb_header(n, skb);
+ 	return n;
+--- linux-2.4.24/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2004-01-06 20:37:33.000000000 +0100
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.24/net/ipv4/ip_output.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/ipv4/ip_output.c	2004-01-06 21:12:40.000000000 +0100
+@@ -883,6 +883,11 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		nf_bridge_put(skb->nf_bridge);
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.24/net/ipv4/netfilter/ipt_LOG.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2004-01-06 20:37:33.000000000 +0100
+@@ -316,6 +316,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.24/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/ipv4/netfilter/Makefile	2004-01-06 20:37:33.000000000 +0100
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.24/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.24-ebt-brnf/net/ipv4/netfilter/Config.in	2004-01-06 20:37:33.000000000 +0100
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/bridge/br_netfilter.c	2004-01-06 21:38:16.000000000 +0100
+@@ -0,0 +1,896 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer (maintainer)	<bdschuym@pandora.be>
++ *
++ *	Changes:
++ *	Apr 29 2003: physdev module support (bdschuym)
++ *	Jun 19 2003: let arptables see bridged ARP traffic (bdschuym)
++ *	Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge
++ *	             (bdschuym)
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->nf_bridge->data))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++#ifdef CONFIG_SYSCTL
++static struct ctl_table_header *brnf_sysctl_header;
++static int brnf_call_iptables = 1;
++static int brnf_call_arptables = 1;
++static int brnf_filter_vlan_tagged = 1;
++#else
++#define brnf_filter_vlan_tagged 1
++#endif
++
++#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) &&    \
++	hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) &&  \
++	brnf_filter_vlan_tagged)
++/*
++#define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) &&   \
++	hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \
++	brnf_filter_vlan_tagged)
++*/
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++	skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	skb->dev = bridge_parent(skb->dev);
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				if (skb->protocol ==
++				    __constant_htons(ETH_P_8021Q)) {
++					skb_push(skb, VLAN_HLEN);
++					skb->nh.raw -= VLAN_HLEN;
++				}
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++			skb->pkt_type = PACKET_HOST;
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	skb->dev = nf_bridge->physindev;
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++#ifdef CONFIG_SYSCTL
++	if (!brnf_call_iptables)
++		return NF_ACCEPT;
++#endif
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)
++					  ((*pskb)->mac.ethernet);
++
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++			goto out;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	} else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++	struct net_device *in;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) {
++		in = nf_bridge->physindev;
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++	} else {
++		in = *((struct net_device **)(skb->cb));
++	}
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
++			skb->dev, br_forward_finish, 1);
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  For IP, we pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module. For ARP, indev and outdev are the
++ * bridge ports.
++ */
++static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++#ifdef CONFIG_SYSCTL
++	if (!skb->nf_bridge)
++		return NF_ACCEPT;
++#endif
++
++	if (skb->protocol != __constant_htons(ETH_P_IP)) {
++		if (!IS_VLAN_IP)
++			return NF_ACCEPT;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	/* The physdev module checks on this */
++	nf_bridge->mask |= BRNF_BRIDGED;
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in),
++		bridge_parent(out), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++/*
++static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++	struct net_device **d = (struct net_device **)(skb->cb);
++
++	if (!brnf_call_arptables)
++		return NF_ACCEPT;
++
++	if (skb->protocol != __constant_htons(ETH_P_ARP)) {
++		if (!IS_VLAN_ARP)
++			return NF_ACCEPT;
++		skb_pull(*pskb, VLAN_HLEN);
++		(*pskb)->nh.raw += VLAN_HLEN;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->nh.arph->ar_pln != 4) {
++		if (IS_VLAN_ARP) {
++			skb_push(*pskb, VLAN_HLEN);
++			(*pskb)->nh.raw -= VLAN_HLEN;
++		}
++		return NF_ACCEPT;
++	}
++	*d = (struct net_device *)in;
++	NF_HOOK(NF_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in,
++		(struct net_device *)out, br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++*/
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_push(skb, VLAN_HLEN);
++		skb->nh.raw -= VLAN_HLEN;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++
++#ifdef CONFIG_SYSCTL
++	if (!skb->nf_bridge)
++		return NF_ACCEPT;
++#endif
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_push(skb, VLAN_HLEN);
++			skb->nh.raw -= VLAN_HLEN;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		struct net_device *realoutdev = bridge_parent(skb->dev);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* iptables should match -o br0.x */
++		if (nf_bridge->netoutdev)
++			realoutdev = nf_bridge->netoutdev;
++#endif
++		okfn = br_nf_local_out_finish;
++		if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++			skb_pull(skb, VLAN_HLEN);
++			(*pskb)->nh.raw += VLAN_HLEN;
++		}
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       realoutdev, okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet);
++	struct net_device *realoutdev = bridge_parent(skb->dev);
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	/* Be very paranoid. This probably won't happen anymore, but let's
++	 * keep the check just to be sure... */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		goto print_error;
++	}
++#endif
++
++#ifdef CONFIG_SYSCTL
++	if (!nf_bridge)
++		return NF_ACCEPT;
++#endif
++
++	if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this). This should now
++	 * be fixed, but let's keep the check around.
++	 */
++	if (skb->dst == NULL) {
++		printk(KERN_CRIT "br_netfilter: skb->dst == NULL.");
++		goto print_error;
++	}
++
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++		skb_pull(skb, VLAN_HLEN);
++		skb->nh.raw += VLAN_HLEN;
++	}
++
++	nf_bridge_save_header(skb);
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	if (nf_bridge->netoutdev)
++		realoutdev = nf_bridge->netoutdev;
++#endif
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		realoutdev, br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++print_error:
++	if (skb->dev != NULL) {
++		printk("[%s]", skb->dev->name);
++		if (has_bridge_parent(skb->dev))
++			printk("[%s]", bridge_parent(skb->dev)->name);
++	}
++	printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
++					      skb->data);
++	return NF_ACCEPT;
++#endif
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if ((*pskb)->nf_bridge &&
++	    !((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++#ifdef CONFIG_SYSCTL
++	if (!brnf_call_iptables && !skb->nf_bridge)
++		return NF_ACCEPT;
++#endif
++
++	if ((out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit)
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++	    || ((out->priv_flags & IFF_802_1Q_VLAN) &&
++	    VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit)
++#endif
++	    ) {
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++		/* the iptables outdev is br0.x, not br0 */
++		if (out->priv_flags & IFF_802_1Q_VLAN)
++			nf_bridge->netoutdev = (struct net_device *)out;
++#endif
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing, 
++	  .pf = PF_BRIDGE, 
++	  .hooknum = NF_BR_PRE_ROUTING, 
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward_ip,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF /*- 1*/, },
++/*	{ .hook = br_nf_forward_arp,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },*/
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++#ifdef CONFIG_SYSCTL
++static
++int brnf_sysctl_call_tables(ctl_table *ctl, int write, struct file * filp,
++			void *buffer, size_t *lenp)
++{
++	int ret;
++
++	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
++
++	if (write && *(int *)(ctl->data))
++		*(int *)(ctl->data) = 1;
++	return ret;
++}
++
++static ctl_table brnf_table[] = {
++	{
++		.ctl_name	= NET_BRIDGE_NF_CALL_ARPTABLES,
++		.procname	= "bridge-nf-call-arptables",
++		.data		= &brnf_call_arptables,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= &brnf_sysctl_call_tables,
++	},
++	{
++		.ctl_name	= NET_BRIDGE_NF_CALL_IPTABLES,
++		.procname	= "bridge-nf-call-iptables",
++		.data		= &brnf_call_iptables,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= &brnf_sysctl_call_tables,
++	},
++	{
++		.ctl_name	= NET_BRIDGE_NF_FILTER_VLAN_TAGGED,
++		.procname	= "bridge-nf-filter-vlan-tagged",
++		.data		= &brnf_filter_vlan_tagged,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= &brnf_sysctl_call_tables,
++	},
++	{ .ctl_name = 0 }
++};
++
++static ctl_table brnf_bridge_table[] = {
++	{
++		.ctl_name	= NET_BRIDGE,
++		.procname	= "bridge",
++		.mode		= 0555,
++		.child		= brnf_table,
++	},
++	{ .ctl_name = 0 }
++};
++
++static ctl_table brnf_net_table[] = {
++	{
++		.ctl_name	= CTL_NET,
++		.procname	= "net",
++		.mode		= 0555,
++		.child		= brnf_bridge_table,
++	},
++	{ .ctl_name = 0 }
++};
++#endif
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++#ifdef CONFIG_SYSCTL
++	brnf_sysctl_header = register_sysctl_table(brnf_net_table, 0);
++	if (brnf_sysctl_header == NULL) {
++		printk(KERN_WARNING "br_netfilter: can't register to sysctl.\n");
++		for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++)
++			nf_unregister_hook(&br_nf_ops[i]);
++		return -EFAULT;
++	}
++#endif
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++#ifdef CONFIG_SYSCTL
++	unregister_sysctl_table(brnf_sysctl_header);
++#endif
++
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2004-01-06 20:37:33.000000000 +0100
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2004-01-06 21:39:26.000000000 +0100
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++	u_int8_t invert;
++	u_int8_t bitmask;
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.24/net/8021q/vlan_dev.c	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/net/8021q/vlan_dev.c	2004-01-06 20:37:33.000000000 +0100
+@@ -507,6 +507,10 @@ int vlan_dev_hard_start_xmit(struct sk_b
+ 	stats->tx_packets++; /* for statics only */
+ 	stats->tx_bytes += skb->len;
+ 
++	skb->protocol = __constant_htons(ETH_P_8021Q);
++	skb->mac.raw -= VLAN_HLEN;
++	skb->nh.raw -= VLAN_HLEN;
++
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+--- linux-2.4.24/include/linux/sysctl.h	2003-11-28 19:26:21.000000000 +0100
++++ linux-2.4.24-ebt-brnf/include/linux/sysctl.h	2004-01-06 21:05:58.000000000 +0100
+@@ -586,6 +586,15 @@ enum {
+ 	NET_DECNET_CONF_DEV_STATE = 7
+ };
+ 
++/* /proc/sys/net/bridge */
++enum {
++	NET_BRIDGE_NF_CALL_ARPTABLES = 1,
++	NET_BRIDGE_NF_CALL_IPTABLES = 2,
++	NET_BRIDGE_NF_CALL_IP6TABLES = 3,
++	NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
++};
++
++
+ /* CTL_PROC names: */
+ 
+ /* CTL_FS names: */
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.21.diff b/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.21.diff
new file mode 100644
index 0000000..4602c72
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.21.diff
@@ -0,0 +1,5354 @@
+--- linux-2.4.21/net/bridge/br_private.h	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/br_private.h	2003-06-25 19:03:44.000000000 +0200
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.21/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/if_bridge.h	2003-06-25 18:56:19.000000000 +0200
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.21/net/core/dev.c	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/core/dev.c	2003-06-25 19:50:58.000000000 +0200
+@@ -1424,7 +1424,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1441,7 +1441,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1461,6 +1460,9 @@ int netif_receive_skb(struct sk_buff *sk
+ 	struct packet_type *ptype, *pt_prev;
+ 	int ret = NET_RX_DROP;
+ 	unsigned short type = skb->protocol;
++#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
++	struct net_device* rx_dev = skb->dev;
++#endif
+ 
+ 	if (skb->stamp.tv_sec == 0)
+ 		do_gettimeofday(&skb->stamp);
+@@ -1497,11 +1499,19 @@ int netif_receive_skb(struct sk_buff *sk
+ 	if (skb->dev->divert && skb->dev->divert->divert)
+ 		ret = handle_diverter(skb);
+ #endif /* CONFIG_NET_DIVERT */
+-			
++
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+-	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++        skb->protocol != __constant_ntohs(ETH_P_8021Q) &&
++#endif
++        br_handle_frame_hook != NULL) {
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+@@ -1526,6 +1536,22 @@ int netif_receive_skb(struct sk_buff *sk
+ 		} else {
+ 			ret = pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
++#if (defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)) && (defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
++		if (skb->protocol == __constant_ntohs(ETH_P_8021Q) && !skb->dev) {
++		    skb->dev = rx_dev;
++
++		    if (skb->dev->br_port != NULL &&
++				br_handle_frame_hook != NULL) {
++				int ret;
++
++				ret = handle_bridge(skb, NULL);
++				if (br_handle_frame_hook(skb) == 0)
++					return ret;
++			}
++
++			kfree_skb(skb);
++		}
++#endif
+ 	} else {
+ 		kfree_skb(skb);
+ 		/* Jamal, now you will not able to escape explaining
+@@ -1897,7 +1923,7 @@ static int dev_proc_stats(char *buffer, 
+  *	are adjusted, %RTM_NEWLINK is sent to the routing socket and the
+  *	function returns zero.
+  */
+- 
++
+ int netdev_set_master(struct net_device *slave, struct net_device *master)
+ {
+ 	struct net_device *old = slave->master;
+--- linux-2.4.21/net/bridge/br_input.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/bridge/br_input.c	2003-06-24 23:51:08.000000000 +0200
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,25 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.21/net/bridge/br_forward.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/bridge/br_forward.c	2003-06-25 22:14:31.000000000 +0200
+@@ -30,18 +30,22 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +53,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.21/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/br.c	2003-06-24 23:49:22.000000000 +0200
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.21/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/Makefile	2003-06-24 23:52:58.000000000 +0200
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.21/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge.h	2003-06-25 19:03:46.000000000 +0200
+@@ -1,11 +1,14 @@
+ #ifndef __LINUX_BRIDGE_NETFILTER_H
+ #define __LINUX_BRIDGE_NETFILTER_H
+ 
+-/* bridge-specific defines for netfilter. 
++/* bridge-specific defines for netfilter.
+  */
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +21,49 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.21/net/Makefile	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/Makefile	2003-06-24 22:36:24.000000000 +0200
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.21/net/Config.in	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/Config.in	2003-06-24 22:35:17.000000000 +0200
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/Makefile	2003-06-24 23:07:01.000000000 +0200
+@@ -0,0 +1,29 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/Config.in	2003-06-24 23:07:07.000000000 +0200
+@@ -0,0 +1,18 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != sizeof(struct ebt_arp_info))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != sizeof(struct ebt_ip_info))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG "ebt_vlan: " __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof(struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen < sizeof(struct ebt_802_3_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != sizeof(struct ebt_pkttype_info))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-06-24 23:33:46.000000000 +0200
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != sizeof(struct ebt_log_info))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-06-24 23:06:55.000000000 +0200
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-06-25 19:16:56.000000000 +0200
+@@ -0,0 +1,359 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-06-24 23:12:37.000000000 +0200
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.21/include/linux/netfilter.h	2001-11-22 20:47:48.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter.h	2003-06-25 20:35:42.000000000 +0200
+@@ -117,28 +117,34 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+-int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
++int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt,
+ 		  int len);
+ int nf_getsockopt(struct sock *sk, int pf, int optval, char *opt,
+ 		  int *len);
+ 
+ /* Packet queuing */
+-typedef int (*nf_queue_outfn_t)(struct sk_buff *skb, 
++typedef int (*nf_queue_outfn_t)(struct sk_buff *skb,
+                                 struct nf_info *info, void *data);
+-extern int nf_register_queue_handler(int pf, 
++extern int nf_register_queue_handler(int pf,
+                                      nf_queue_outfn_t outfn, void *data);
+ extern int nf_unregister_queue_handler(int pf);
+ extern void nf_reinject(struct sk_buff *skb,
+--- linux-2.4.21/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_ipv4.h	2003-06-25 19:02:33.000000000 +0200
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.21/include/linux/skbuff.h	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/include/linux/skbuff.h	2003-06-25 20:32:39.000000000 +0200
+@@ -92,6 +92,17 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -204,6 +215,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -278,7 +292,7 @@ static inline struct sk_buff *skb_get(st
+  * If users==1, we are the only owner and are can avoid redundant
+  * atomic change.
+  */
+- 
++
+ /**
+  *	kfree_skb - free an sk_buff
+  *	@skb: buffer to free
+@@ -286,7 +300,7 @@ static inline struct sk_buff *skb_get(st
+  *	Drop a reference to the buffer and free it if the usage count has
+  *	hit zero.
+  */
+- 
++
+ static inline void kfree_skb(struct sk_buff *skb)
+ {
+ 	if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
+@@ -1165,6 +1179,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.21/net/core/netfilter.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/core/netfilter.c	2003-06-25 18:57:09.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* netfilter.c: look after the filters for various protocols. 
++/* netfilter.c: look after the filters for various protocols.
+  * Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
+  *
+  * Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -365,7 +370,7 @@ static unsigned int nf_iterate(struct li
+ 			break;
+ 
+ 		default:
+-			NFDEBUG("Evil return from %p(%u).\n", 
++			NFDEBUG("Evil return from %p(%u).\n",
+ 				elem->hook, hook);
+ #endif
+ 		}
+@@ -374,7 +379,7 @@ static unsigned int nf_iterate(struct li
+ }
+ 
+ int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
+-{      
++{
+ 	int ret;
+ 
+ 	br_write_lock_bh(BR_NETPROTO_LOCK);
+@@ -400,12 +405,12 @@ int nf_unregister_queue_handler(int pf)
+ 	return 0;
+ }
+ 
+-/* 
+- * Any packet that leaves via this function must come back 
++/*
++ * Any packet that leaves via this function must come back
+  * through nf_reinject().
+  */
+-static void nf_queue(struct sk_buff *skb, 
+-		     struct list_head *elem, 
++static void nf_queue(struct sk_buff *skb,
++		     struct list_head *elem,
+ 		     int pf, unsigned int hook,
+ 		     struct net_device *indev,
+ 		     struct net_device *outdev,
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -428,18 +437,31 @@ static void nf_queue(struct sk_buff *skb
+ 		return;
+ 	}
+ 
+-	*info = (struct nf_info) { 
++	*info = (struct nf_info) {
+ 		(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
+ 
+ 	/* Bump dev refs so they don't vanish while packet is out */
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -528,9 +551,9 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	if (verdict == NF_ACCEPT) {
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+-				     &skb, info->hook, 
++				     &skb, info->hook,
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+@@ -539,20 +562,27 @@ void nf_reinject(struct sk_buff *skb, st
+ 		break;
+ 
+ 	case NF_QUEUE:
+-		nf_queue(skb, elem, info->pf, info->hook, 
++		nf_queue(skb, elem, info->pf, info->hook,
+ 			 info->indev, info->outdev, info->okfn);
+ 		break;
+-
+-	case NF_DROP:
+-		kfree_skb(skb);
+-		break;
+ 	}
+ 	br_read_unlock_bh(BR_NETPROTO_LOCK);
+ 
+ 	/* Release those devices we held, or Alexey will kill me. */
+ 	if (info->indev) dev_put(info->indev);
+ 	if (info->outdev) dev_put(info->outdev);
+-	
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
++
++	if (verdict == NF_DROP)
++		kfree_skb(skb);
++
+ 	kfree(info);
+ 	return;
+ }
+--- linux-2.4.21/net/core/skbuff.c	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/core/skbuff.c	2003-06-25 00:04:08.000000000 +0200
+@@ -245,6 +245,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -325,6 +328,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -391,6 +397,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -403,6 +412,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -437,6 +449,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.21/net/ipv4/netfilter/ip_tables.c	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-06-25 00:08:39.000000000 +0200
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.21/net/ipv4/ip_output.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/ipv4/ip_output.c	2003-06-25 00:09:31.000000000 +0200
+@@ -879,6 +879,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.21/net/ipv4/netfilter/ipt_LOG.c	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-06-25 00:10:34.000000000 +0200
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.21/net/ipv4/netfilter/Makefile	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/ipv4/netfilter/Makefile	2003-06-25 18:52:45.000000000 +0200
+@@ -84,6 +84,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+@@ -100,6 +102,7 @@ obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt
+ 
+ # generic ARP tables
+ obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o
++obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o
+ 
+ # just filtering instance of ARP tables for now
+ obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
+--- linux-2.4.21/net/ipv4/netfilter/Config.in	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/ipv4/netfilter/Config.in	2003-06-25 18:53:55.000000000 +0200
+@@ -43,6 +43,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/bridge/br_netfilter.c	2003-06-25 19:16:47.000000000 +0200
+@@ -0,0 +1,636 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bdschuym@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_BRIDGED; /* The physdev module checks on this */
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_PRE_ROUTING,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-06-25 19:24:53.000000000 +0200
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.21-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-06-25 18:42:04.000000000 +0200
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.21/net/8021q/vlan_dev.c	2003-06-13 16:51:39.000000000 +0200
++++ linux-2.4.21-ebt-brnf/net/8021q/vlan_dev.c	2003-06-25 19:46:59.000000000 +0200
+@@ -149,7 +149,9 @@ int vlan_skb_recv(struct sk_buff *skb, s
+ 		printk(VLAN_DBG "%s: ERROR: No net_device for VID: %i on dev: %s [%i]\n",
+ 			__FUNCTION__, (unsigned int)(vid), dev->name, dev->ifindex);
+ #endif
++#if !(defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE))
+ 		kfree_skb(skb);
++#endif
+ 		return -1;
+ 	}
+ 
diff --git a/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.22.diff b/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.22.diff
new file mode 100644
index 0000000..155a19b
--- /dev/null
+++ b/kernel/patches/ebtables-brnf/ebtables-brnf_vs_2.4.22.diff
@@ -0,0 +1,11047 @@
+--- linux-2.4.22/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_private.h	2003-09-03 20:06:25.000000000 +0200
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.22/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/if_bridge.h	2003-09-03 20:06:59.000000000 +0200
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.22/net/core/dev.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/dev.c	2003-09-03 20:08:46.000000000 +0200
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.22/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_input.c	2003-09-03 20:10:57.000000000 +0200
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.22/net/bridge/br_forward.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_forward.c	2003-09-03 20:12:27.000000000 +0200
+@@ -30,18 +30,22 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +53,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.22/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br.c	2003-09-03 20:13:38.000000000 +0200
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.22/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/Makefile	2003-09-03 20:14:17.000000000 +0200
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.22/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge.h	2003-09-03 20:15:34.000000000 +0200
+@@ -6,6 +6,9 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +21,49 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.22/net/Makefile	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Makefile	2003-09-03 20:16:29.000000000 +0200
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.22/net/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Config.in	2003-09-03 20:17:08.000000000 +0200
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Makefile	2003-09-03 21:10:21.000000000 +0200
+@@ -0,0 +1,31 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Config.in	2003-09-03 21:10:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.22/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter.h	2003-09-03 20:20:40.000000000 +0200
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.22/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4.h	2003-09-03 20:25:14.000000000 +0200
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.22/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/skbuff.h	2003-09-03 20:26:39.000000000 +0200
+@@ -92,6 +92,17 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +219,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1183,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.22/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/netfilter.c	2003-09-03 20:30:15.000000000 +0200
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.22/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/skbuff.c	2003-09-03 20:31:51.000000000 +0200
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.22/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-09-03 20:36:13.000000000 +0200
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.22/net/ipv4/ip_output.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/ip_output.c	2003-09-03 20:36:57.000000000 +0200
+@@ -882,6 +882,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.22/net/ipv4/netfilter/ipt_LOG.c	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-09-03 20:37:40.000000000 +0200
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.22/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Makefile	2003-09-03 20:38:15.000000000 +0200
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.22/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Config.in	2003-09-03 20:39:04.000000000 +0200
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br_netfilter.c	2003-09-03 21:14:20.000000000 +0200
+@@ -0,0 +1,636 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bdschuym@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_BRIDGED; /* The physdev module checks on this */
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_PRE_ROUTING,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-09-03 21:15:28.000000000 +0200
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-09-03 21:15:59.000000000 +0200
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
+--- linux-2.4.22/net/bridge/br_private.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_private.h	2003-09-03 20:06:25.000000000 +0200
+@@ -144,8 +144,10 @@ extern void br_fdb_insert(struct net_bri
+ /* br_forward.c */
+ extern void br_deliver(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_dev_queue_push_xmit(struct sk_buff *skb);
+ extern void br_forward(struct net_bridge_port *to,
+ 		struct sk_buff *skb);
++extern int br_forward_finish(struct sk_buff *skb);
+ extern void br_flood_deliver(struct net_bridge *br,
+ 		      struct sk_buff *skb,
+ 		      int clone);
+@@ -166,7 +168,8 @@ extern void br_get_port_ifindices(struct
+ 			   int *ifindices);
+ 
+ /* br_input.c */
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame_finish(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+@@ -177,6 +180,10 @@ extern int br_ioctl(struct net_bridge *b
+ 	     unsigned long arg2);
+ extern int br_ioctl_deviceless_stub(unsigned long arg);
+ 
++/* br_netfilter.c */
++extern int br_netfilter_init(void);
++extern void br_netfilter_fini(void);
++
+ /* br_stp.c */
+ extern int br_is_root_bridge(struct net_bridge *br);
+ extern struct net_bridge_port *br_get_port(struct net_bridge *br,
+--- linux-2.4.22/include/linux/if_bridge.h	2001-11-22 20:47:12.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/if_bridge.h	2003-09-03 20:06:59.000000000 +0200
+@@ -102,7 +102,8 @@ struct net_bridge;
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
+ 
+ #endif
+ 
+--- linux-2.4.22/net/core/dev.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/dev.c	2003-09-03 20:08:46.000000000 +0200
+@@ -1426,7 +1426,7 @@ static void net_tx_action(struct softirq
+ 
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1443,7 +1443,6 @@ static __inline__ int handle_bridge(stru
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1503,8 +1502,13 @@ int netif_receive_skb(struct sk_buff *sk
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 	if (skb->dev->br_port != NULL &&
+ 	    br_handle_frame_hook != NULL) {
+-		return handle_bridge(skb, pt_prev);
+-	}
++		int ret;
++
++		ret = handle_bridge(skb, pt_prev);
++		if (br_handle_frame_hook(skb) == 0)
++			return ret;
++		pt_prev = NULL;
++ 	}
+ #endif
+ 
+ 	for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+--- linux-2.4.22/net/bridge/br_input.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_input.c	2003-09-03 20:10:57.000000000 +0200
+@@ -24,6 +24,9 @@ unsigned char bridge_ula[6] = { 0x01, 0x
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -46,7 +49,7 @@ static void br_pass_frame_up(struct net_
+ 			br_pass_frame_up_finish);
+ }
+ 
+-static int br_handle_frame_finish(struct sk_buff *skb)
++int br_handle_frame_finish(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -112,7 +115,7 @@ err_nolock:
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,26 +149,35 @@ void br_handle_frame(struct sk_buff *skb
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
++			return -1;
++		}
++
++		if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
++			skb->pkt_type = PACKET_HOST;
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL,
+ 			br_stp_handle_bpdu);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ 	read_unlock(&br->lock);
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux-2.4.22/net/bridge/br_forward.c	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/bridge/br_forward.c	2003-09-03 20:12:27.000000000 +0200
+@@ -30,18 +30,22 @@ static inline int should_deliver(struct 
+ 	return 1;
+ }
+ 
+-static int __dev_queue_push_xmit(struct sk_buff *skb)
++int br_dev_queue_push_xmit(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER
++	if (skb->nf_bridge)
++		memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
++#endif
+ 	skb_push(skb, ETH_HLEN);
+ 	dev_queue_xmit(skb);
+ 
+ 	return 0;
+ }
+ 
+-static int __br_forward_finish(struct sk_buff *skb)
++int br_forward_finish(struct sk_buff *skb)
+ {
+ 	NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+-			__dev_queue_push_xmit);
++			br_dev_queue_push_xmit);
+ 
+ 	return 0;
+ }
+@@ -49,8 +53,11 @@ static int __br_forward_finish(struct sk
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
+@@ -61,7 +68,7 @@ static void __br_forward(struct net_brid
+ 	skb->dev = to->dev;
+ 
+ 	NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+-			__br_forward_finish);
++			br_forward_finish);
+ }
+ 
+ /* called under bridge lock */
+--- linux-2.4.22/net/bridge/br.c	2002-11-29 00:53:15.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br.c	2003-09-03 20:13:38.000000000 +0200
+@@ -29,6 +29,8 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -43,6 +45,10 @@ static int __init br_init(void)
+ {
+ 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ 
++#ifdef CONFIG_NETFILTER
++	if (br_netfilter_init())
++		return 1;
++#endif
+ 	br_handle_frame_hook = br_handle_frame;
+ 	br_ioctl_hook = br_ioctl_deviceless_stub;
+ #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+@@ -61,6 +67,9 @@ static void __br_clear_ioctl_hook(void)
+ 
+ static void __exit br_deinit(void)
+ {
++#ifdef CONFIG_NETFILTER
++	br_netfilter_fini();
++#endif
+ 	unregister_netdevice_notifier(&br_device_notifier);
+ 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
+ 
+@@ -74,7 +83,7 @@ static void __exit br_deinit(void)
+ #endif
+ }
+ 
+-EXPORT_NO_SYMBOLS;
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.22/net/bridge/Makefile	2000-12-29 23:07:24.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/Makefile	2003-09-03 20:14:17.000000000 +0200
+@@ -7,10 +7,17 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++export-objs := br.o
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+ 			br_stp_if.o br_stp_timer.o
++
++ifeq ($(CONFIG_NETFILTER),y)
++obj-y		+= br_netfilter.o
++endif
++
+ obj-m		:= $(O_TARGET)
+ 
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.22/include/linux/netfilter_bridge.h	2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge.h	2003-09-03 20:15:34.000000000 +0200
+@@ -6,6 +6,9 @@
+ 
+ #include <linux/config.h>
+ #include <linux/netfilter.h>
++#if defined(__KERNEL__) && defined(CONFIG_NETFILTER)
++#include <asm/atomic.h>
++#endif
+ 
+ /* Bridge Hooks */
+ /* After promisc drops, checksum checks. */
+@@ -18,7 +21,49 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
++
++#ifdef __KERNEL__
++
++#define BRNF_PKT_TYPE			0x01
++#define BRNF_BRIDGED_DNAT		0x02
++#define BRNF_DONT_TAKE_PARENT		0x04
++#define BRNF_BRIDGED			0x08
++
++enum nf_br_hook_priorities {
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_BRNF = 0,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
++};
++
++#ifdef CONFIG_NETFILTER
++static inline
++struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
++{
++	struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
++
++	if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
++		atomic_set(&(*nf_bridge)->use, 1);
++		(*nf_bridge)->mask = 0;
++		(*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
++	}
++
++	return *nf_bridge;
++}
+ 
++struct bridge_skb_cb {
++	union {
++		__u32 ipv4;
++	} daddr;
++};
++#endif /* CONFIG_NETFILTER */
+ 
++#endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.22/net/Makefile	2002-08-03 02:39:46.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Makefile	2003-09-03 20:16:29.000000000 +0200
+@@ -7,7 +7,8 @@
+ 
+ O_TARGET :=	network.o
+ 
+-mod-subdirs :=	ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core
++mod-subdirs :=	ipv4/netfilter ipv6/netfilter bridge/netfilter ipx irda \
++	bluetooth atm netlink sched core
+ export-objs :=	netsyms.o
+ 
+ subdir-y :=	core ethernet
+@@ -26,6 +27,12 @@ subdir-$(CONFIG_NETFILTER)	+= ipv6/netfi
+ endif
+ endif
+ 
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD)		+= khttpd
+ subdir-$(CONFIG_PACKET)		+= packet
+ subdir-$(CONFIG_NET_SCHED)	+= sched
+--- linux-2.4.22/net/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/Config.in	2003-09-03 20:17:08.000000000 +0200
+@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++   source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Makefile	2003-09-03 21:10:21.000000000 +0200
+@@ -0,0 +1,31 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET	:= netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o
++obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_stp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/Config.in	2003-09-03 21:10:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.3 filter support' CONFIG_BRIDGE_EBT_802_3 $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: packet type filter support' CONFIG_BRIDGE_EBT_PKTTYPE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: STP filter support' CONFIG_BRIDGE_EBT_STP $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: arp reply target support' CONFIG_BRIDGE_EBT_ARPREPLY $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_filter.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,90 @@
++/*
++ *  ebtable_filter
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~FILTER_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_filter =
++{ 
++  {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS, 
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++	int i, j, ret;
++
++	ret = ebt_register_table(&frame_filter);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_filter[j]);
++	ebt_unregister_table(&frame_filter);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++		nf_unregister_hook(&ebt_ops_filter[i]);
++	ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_nat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ *  ebtable_nat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++  "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++  { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~NAT_VALID_HOOKS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++  {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++	int i, ret, j;
++
++	ret = ebt_register_table(&frame_nat);
++	if (ret < 0)
++		return ret;
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++			goto cleanup;
++	return ret;
++cleanup:
++	for (j = 0; j < i; j++)
++		nf_unregister_hook(&ebt_ops_nat[j]);
++	ebt_unregister_table(&frame_nat);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	int i;
++
++	for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++		nf_unregister_hook(&ebt_ops_nat[i]);
++	ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtable_broute.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static int ebt_broute(struct sk_buff **pskb)
++{
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	br_should_route_hook = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arpreply.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,86 @@
++/*
++ *  ebt_arpreply
++ *
++ *	Authors:
++ *	Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  August, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arpreply.h>
++#include <linux/if_arp.h>
++#include <net/arp.h>
++#include <linux/module.h>
++
++static int ebt_target_reply(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++	struct arphdr *ah;
++	unsigned char *sha, *arp_ptr;
++	u32 sip, tip;
++
++	ah = (**pskb).nh.arph;
++	if (ah->ar_op != __constant_htons(ARPOP_REQUEST) ||
++	    ah->ar_hln != ETH_ALEN || ah->ar_pro != htons(ETH_P_IP) ||
++	    ah->ar_pln != 4)
++		return EBT_CONTINUE;
++
++	arp_ptr = (unsigned char *)(ah + 1);
++
++	/* get source and target IP */
++	sha = arp_ptr;
++	arp_ptr += ETH_ALEN;
++	memcpy(&sip, arp_ptr, 4);
++	arp_ptr += 4 + ETH_ALEN;
++	memcpy(&tip, arp_ptr, 4);
++
++	arp_send(ARPOP_REPLY, ETH_P_ARP, sip, in, tip, sha, info->mac, sha);
++
++	return info->target;
++}
++
++static int ebt_target_reply_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arpreply_info *info = (struct ebt_arpreply_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arpreply_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_ARP) ||
++	    e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target reply_target =
++{
++	.name		= EBT_ARPREPLY_TARGET,
++	.target		= ebt_target_reply,
++	.check		= ebt_target_reply_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&reply_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&reply_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_802_3.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * 802_3
++ *
++ * Author:
++ * Chris Vitale csv@bluetail.com
++ *
++ * May 2003
++ * 
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_802_3.h>
++#include <linux/module.h>
++
++static int ebt_filter_802_3(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++	struct ebt_802_3_hdr *hdr = (struct ebt_802_3_hdr *)skb->mac.ethernet;
++	uint16_t type = hdr->llc.ui.ctrl & IS_UI ? hdr->llc.ui.type : hdr->llc.ni.type;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		if (FWINV(info->sap != hdr->llc.ui.ssap, EBT_802_3_SAP)) 
++				return EBT_NOMATCH;
++		if (FWINV(info->sap != hdr->llc.ui.dsap, EBT_802_3_SAP))
++				return EBT_NOMATCH;
++	}
++
++	if (info->bitmask & EBT_802_3_TYPE) {
++		if (!(hdr->llc.ui.dsap == CHECK_TYPE && hdr->llc.ui.ssap == CHECK_TYPE))
++			return EBT_NOMATCH;
++		if (FWINV(info->type != type, EBT_802_3_TYPE)) 
++			return EBT_NOMATCH;
++	}
++
++	return EBT_MATCH;
++}
++
++static struct ebt_match filter_802_3;
++static int ebt_802_3_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_802_3_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_802_3_MASK || info->invflags & ~EBT_802_3_MASK)
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_802_3 =
++{
++	.name		= EBT_802_3_MATCH,
++	.match		= ebt_filter_802_3,
++	.check		= ebt_802_3_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_802_3);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_802_3);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ *  ebt_mark
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/module.h>
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return info->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_t_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_mark_m.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,61 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_mark_m_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_pkttype.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ *  ebt_pkttype
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *
++ *  April, 2003
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++#include <linux/module.h>
++
++static int ebt_filter_pkttype(const struct sk_buff *skb,
++   const struct net_device *in,
++   const struct net_device *out,
++   const void *data,
++   unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	return (skb->pkt_type != info->pkt_type) ^ info->invert;
++}
++
++static int ebt_pkttype_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_pkttype_info *info = (struct ebt_pkttype_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_pkttype_info)))
++		return -EINVAL;
++	if (info->invert != 0 && info->invert != 1)
++		return -EINVAL;
++	/* Allow any pkt_type value */
++	return 0;
++}
++
++static struct ebt_match filter_pkttype =
++{
++	.name		= EBT_PKTTYPE_MATCH,
++	.match		= ebt_filter_pkttype,
++	.check		= ebt_pkttype_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_pkttype);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_pkttype);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_stp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,191 @@
++/*
++ *  ebt_stp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bdschuym@pandora.be>
++ *	Stephen Hemminger <shemminger@osdl.org>
++ *
++ *  June, 2003
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_stp.h>
++#include <linux/module.h>
++
++#define BPDU_TYPE_CONFIG 0
++#define BPDU_TYPE_TCN 0x80
++
++struct stp_header {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t pid;
++	uint8_t vers;
++	uint8_t type;
++};
++
++struct stp_config_pdu {
++	uint8_t flags;
++	uint8_t root[8];
++	uint8_t root_cost[4];
++	uint8_t sender[8];
++	uint8_t port[2];
++	uint8_t msg_age[2];
++	uint8_t max_age[2];
++	uint8_t hello_time[2];
++	uint8_t forward_delay[2];
++};
++
++#define NR16(p) (p[0] << 8 | p[1])
++#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
++
++static int ebt_filter_config(struct ebt_stp_info *info,
++   struct stp_config_pdu *stpc)
++{
++	struct ebt_stp_config_info *c;
++	uint16_t v16;
++	uint32_t v32;
++	int verdict, i;
++
++	c = &info->config;
++	if ((info->bitmask & EBT_STP_FLAGS) &&
++	    FWINV(c->flags != stpc->flags, EBT_STP_FLAGS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_STP_ROOTPRIO) {
++		v16 = NR16(stpc->root);
++		if (FWINV(v16 < c->root_priol ||
++		    v16 > c->root_priou, EBT_STP_ROOTPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->root[2+i] ^ c->root_addr[i]) &
++			           c->root_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_ROOTADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_ROOTCOST) {
++		v32 = NR32(stpc->root_cost);
++		if (FWINV(v32 < c->root_costl ||
++		    v32 > c->root_costu, EBT_STP_ROOTCOST))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERPRIO) {
++		v16 = NR16(stpc->sender);
++		if (FWINV(v16 < c->sender_priol ||
++		    v16 > c->sender_priou, EBT_STP_SENDERPRIO))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_SENDERADDR) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (stpc->sender[2+i] ^ c->sender_addr[i]) &
++			           c->sender_addrmsk[i];
++		if (FWINV(verdict != 0, EBT_STP_SENDERADDR))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_PORT) {
++		v16 = NR16(stpc->port);
++		if (FWINV(v16 < c->portl ||
++		    v16 > c->portu, EBT_STP_PORT))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MSGAGE) {
++		v16 = NR16(stpc->msg_age);
++		if (FWINV(v16 < c->msg_agel ||
++		    v16 > c->msg_ageu, EBT_STP_MSGAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_MAXAGE) {
++		v16 = NR16(stpc->max_age);
++		if (FWINV(v16 < c->max_agel ||
++		    v16 > c->max_ageu, EBT_STP_MAXAGE))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_HELLOTIME) {
++		v16 = NR16(stpc->hello_time);
++		if (FWINV(v16 < c->hello_timel ||
++		    v16 > c->hello_timeu, EBT_STP_HELLOTIME))
++			return EBT_NOMATCH;
++	}
++	if (info->bitmask & EBT_STP_FWDD) {
++		v16 = NR16(stpc->forward_delay);
++		if (FWINV(v16 < c->forward_delayl ||
++		    v16 > c->forward_delayu, EBT_STP_FWDD))
++			return EBT_NOMATCH;
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_filter_stp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	struct stp_header stph;
++	uint8_t header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
++	if (skb_copy_bits(skb, 0, &stph, sizeof(stph)))
++		return EBT_NOMATCH;
++
++	/* The stp code only considers these */
++	if (memcmp(&stph, header, sizeof(header)))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & EBT_STP_TYPE
++	    && FWINV(info->type != stph.type, EBT_STP_TYPE))
++		return EBT_NOMATCH;
++
++	if (stph.type == BPDU_TYPE_CONFIG &&
++	    info->bitmask & EBT_STP_CONFIG_MASK) {
++		struct stp_config_pdu stpc;
++
++		if (skb_copy_bits(skb, sizeof(stph), &stpc, sizeof(stpc)))
++		    return EBT_NOMATCH;
++		return ebt_filter_config(info, &stpc);
++	}
++	return EBT_MATCH;
++}
++
++static int ebt_stp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_stp_info *info = (struct ebt_stp_info *)data;
++	int len = EBT_ALIGN(sizeof(struct ebt_stp_info));
++	uint8_t bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++	uint8_t msk[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
++
++	if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK ||
++	    !(info->bitmask & EBT_STP_MASK))
++		return -EINVAL;
++	if (datalen != len)
++		return -EINVAL;
++	/* Make sure the match only receives stp frames */
++	if (memcmp(e->destmac, bridge_ula, ETH_ALEN) ||
++	    memcmp(e->destmsk, msk, ETH_ALEN) || !(e->bitmask & EBT_DESTMAC))
++		return -EINVAL;
++
++	return 0;
++}
++
++static struct ebt_match filter_stp =
++{
++	.name		= EBT_STP_MATCH,
++	.match		= ebt_filter_stp,
++	.check		= ebt_stp_check,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_stp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_stp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_redirect.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
++	return info->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_redirect_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_arp.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,149 @@
++/*
++ *  ebt_arp
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *	Tim Gardner <timg@tpi.com>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
++	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
++	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
++	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++		return EBT_NOMATCH;
++
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
++
++		if (info->bitmask & EBT_ARP_SRC_IP) {
++			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
++			   EBT_ARP_SRC_IP))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_IP) {
++			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++			   (2*(((*skb).nh.arph)->ar_hln)) +
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
++			   EBT_ARP_DST_IP))
++				return EBT_NOMATCH;
++		}
++	}
++
++	if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC))
++	{
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		unsigned char dst[ETH_ALEN];
++		unsigned char src[ETH_ALEN];
++
++		// Make sure the packet is long enough.
++		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++			return EBT_NOMATCH;
++		// MAC addresses are 6 bytes.
++		if (((*skb).nh.arph)->ar_hln != ETH_ALEN)
++			return EBT_NOMATCH;
++		if (info->bitmask & EBT_ARP_SRC_MAC) {
++			uint8_t verdict, i;
++
++			memcpy(&src, ((*skb).nh.raw) +
++					sizeof(struct arphdr),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (src[i] ^ info->smaddr[i]) &
++				       info->smmsk[i];	
++			if (FWINV(verdict != 0, EBT_ARP_SRC_MAC))
++				return EBT_NOMATCH;
++		}
++
++		if (info->bitmask & EBT_ARP_DST_MAC) { 
++			uint8_t verdict, i;
++
++			memcpy(&dst, ((*skb).nh.raw) +
++					sizeof(struct arphdr) +
++			   		(((*skb).nh.arph)->ar_hln) +
++			   		(((*skb).nh.arph)->ar_pln),
++					ETH_ALEN);
++			verdict = 0;
++			for (i = 0; i < 6; i++)
++				verdict |= (dst[i] ^ info->dmaddr[i]) &
++					info->dmmsk[i];
++			if (FWINV(verdict != 0, EBT_ARP_DST_MAC))
++				return EBT_NOMATCH;
++		}
++	}
++
++	return EBT_MATCH;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_arp_info)))
++		return -EINVAL;
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++	{NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_ip.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/module.h>
++
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_ip_info)))
++		return -EINVAL;
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
++		return -EINVAL;
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++	{NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_vlan.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,259 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.6"
++
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
++
++
++#define DEBUG_MSG(args...) if (debug) printk (KERN_DEBUG "ebt_vlan: " args)
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
++
++	/*
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * Checking VLAN Identifier (VID)
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
++	 */
++	return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table entry to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
++
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_vlan_info))) {
++		DEBUG_MSG
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
++		return -EINVAL;
++	}
++
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
++	if (info->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (info->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
++				DEBUG_MSG
++				    ("id %d is out of range (1-4096)\n",
++				     info->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
++			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
++		}
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_check_vlan,
++	NULL,
++	THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ */
++static int __init init(void)
++{
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ */
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_vlan);
++}
++
++module_init(init);
++module_exit(fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_log.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,152 @@
++/*
++ *  ebt_log
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/in.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_log_info)))
++		return -EINVAL;
++	if (info->bitmask & ~EBT_LOG_MASK)
++		return -EINVAL;
++	if (info->loglevel >= 8)
++		return -EINVAL;
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	return 0;
++}
++
++struct tcpudphdr
++{
++	uint16_t src;
++	uint16_t dst;
++};
++
++struct arppayload
++{
++	unsigned char mac_src[ETH_ALEN];
++	unsigned char ip_src[4];
++	unsigned char mac_dst[ETH_ALEN];
++	unsigned char ip_dst[4];
++};
++
++static void print_MAC(unsigned char *p)
++{
++	int i;
++
++	for (i = 0; i < ETH_ALEN; i++, p++)
++		printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
++}
++
++#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
++{
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
++	char level_string[4] = "< >";
++	level_string[1] = '0' + info->loglevel;
++
++	spin_lock_bh(&ebt_log_lock);
++	printk(level_string);
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
++
++	printk("MAC source = ");
++	print_MAC((skb->mac.ethernet)->h_source);
++	printk("MAC dest = ");
++	print_MAC((skb->mac.ethernet)->h_dest);
++
++	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	   htons(ETH_P_IP)){
++		struct iphdr *iph = skb->nh.iph;
++		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++		if (iph->protocol == IPPROTO_TCP ||
++		    iph->protocol == IPPROTO_UDP) {
++			struct tcpudphdr *ports = (struct tcpudphdr *)(skb->data + iph->ihl*4);
++
++			if (skb->data + iph->ihl*4 > skb->tail) {
++				printk(" INCOMPLETE TCP/UDP header");
++				goto out;
++			}
++			printk(" SPT=%u DPT=%u", ntohs(ports->src),
++			   ntohs(ports->dst));
++		}
++		goto out;
++	}
++
++	if ((info->bitmask & EBT_LOG_ARP) &&
++	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++		struct arphdr * arph = skb->nh.arph;
++		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++		   ntohs(arph->ar_op));
++		/* If it's for Ethernet and the lengths are OK,
++		 * then log the ARP payload */
++		if (arph->ar_hrd == __constant_htons(1) &&
++		    arph->ar_hln == ETH_ALEN &&
++		    arph->ar_pln == sizeof(uint32_t)) {
++			struct arppayload *arpp = (struct arppayload *)(skb->data + sizeof(*arph));
++
++			if (skb->data + sizeof(*arph) > skb->tail) {
++				printk(" INCOMPLETE ARP header");
++				goto out;
++			}
++
++			printk(" ARP MAC SRC=");
++			print_MAC(arpp->mac_src);
++			printk(" ARP IP SRC=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_src));
++			printk(" ARP MAC DST=");
++			print_MAC(arpp->mac_dst);
++			printk(" ARP IP DST=%u.%u.%u.%u",
++			       myNIPQUAD(arpp->ip_dst));
++		}
++
++	}
++out:
++	printk("\n");
++	spin_unlock_bh(&ebt_log_lock);
++}
++
++static struct ebt_watcher log =
++{
++	{NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_snat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
++
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebt_dnat.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,65 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return info->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
++
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++		return -EINVAL;
++	if (datalen != EBT_ALIGN(sizeof(struct ebt_nat_info)))
++		return -EINVAL;
++	if (INVALID_TARGET)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/netfilter/ebtables.c	2003-09-03 21:10:16.000000000 +0200
+@@ -0,0 +1,1490 @@
++/*
++ *  ebtables
++ *
++ *  Author:
++ *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, July, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ *  This program is free software; you can redistribute it and/or
++ *  modify it under the terms of the GNU General Public License
++ *  as published by the Free Software Foundation; either version
++ *  2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++                                         "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++                                         ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	w->u.watcher->watcher(skb, in, out, w->data,
++	   w->watcher_size);
++	// watchers don't give a verdict
++	return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out)
++{
++	return m->u.match->match(skb, in, out, m->data,
++	   m->match_size);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++	if (*entry == '\0')
++		return 0;
++	if (!device)
++		return 1;
++	return !!strcmp(entry, device->name);
++}
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
++		return 1;
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++
++	if (e->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (e->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table)
++{
++	int i, nentries;
++	struct ebt_entry *point;
++	struct ebt_counter *counter_base, *cb_base;
++	struct ebt_entry_target *t;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
++	struct ebt_table_info *private = table->private;
++
++	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
++	   cpu_number_map(smp_processor_id()));
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++	// base for chain jumps
++	base = private->entries;
++	i = 0;
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
++
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
++			goto letscontinue;
++
++		// increase counter
++		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
++
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out);
++
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
++letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
++				goto letscontinue;
++			}
++#endif
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
++			nentries = chaininfo->nentries;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
++			continue;
++		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++#endif
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
++letscontinue:
++		point = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i++;
++	}
++
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
++		read_unlock_bh(&table->lock);
++		return NF_ACCEPT;
++	}
++	read_unlock_bh(&table->lock);
++	return NF_DROP;
++}
++
++// If it succeeds, returns element and locks mutex
++static inline void *
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_match *match;
++	int ret;
++
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match)
++		return ret;
++	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
++	if (match->check &&
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++		BUGPRINT("match->check failed\n");
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++   const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++	struct ebt_watcher *watcher;
++	int ret;
++
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher)
++		return ret;
++	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
++	if (watcher->check &&
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++		BUGPRINT("watcher->check failed\n");
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
++		return -EINVAL;
++	}
++	(*cnt)++;
++	return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++   struct ebt_table_info *newinfo, char *base, char *limit,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++	int i;
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ( (char *)hook_entries[i] - base ==
++		   (char *)e - newinfo->entries)
++			break;
++	}
++	// beginning of a new chain
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++			// we make userspace set this right,
++			// so there is no misunderstanding
++			BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++			         "in distinguisher\n");
++			return -EINVAL;
++		}
++		// this checks if the previous chain has as many entries
++		// as it said it has
++		if (*n != *cnt) {
++			BUGPRINT("nentries does not equal the nr of entries "
++		                 "in the chain\n");
++			return -EINVAL;
++		}
++		// before we look at the struct, be sure it is not too big
++		if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++		   > limit) {
++			BUGPRINT("entries_size too small\n");
++			return -EINVAL;
++		}
++		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
++			return -EINVAL;
++		}
++		*n = ((struct ebt_entries *)e)->nentries;
++		*cnt = 0;
++		return 0;
++	}
++	// a plain old entry, heh
++	if (sizeof(struct ebt_entry) > e->watchers_offset ||
++	   e->watchers_offset > e->target_offset ||
++	   e->target_offset >= e->next_offset) {
++		BUGPRINT("entry offsets not in right order\n");
++		return -EINVAL;
++	}
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
++		return -EINVAL;
++	}
++
++	(*cnt)++;
++	(*totalcnt)++;
++	return 0;
++}
++
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (m->u.match->destroy)
++		m->u.match->destroy(m->data, m->match_size);
++	if (m->u.match->me)
++		__MOD_DEC_USE_COUNT(m->u.match->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++	if (i && (*i)-- == 0)
++		return 1;
++	if (w->u.watcher->destroy)
++		w->u.watcher->destroy(w->data, w->watcher_size);
++	if (w->u.watcher->me)
++		__MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++	return 0;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++	struct ebt_entry_target *t;
++	struct ebt_target *target;
++	unsigned int i, j, hook = 0, hookmask = 0;
++	int ret;
++
++	// Don't mess with the struct ebt_entries
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	if (e->bitmask & ~EBT_F_MASK) {
++		BUGPRINT("Unknown flag for bitmask\n");
++		return -EINVAL;
++	}
++	if (e->invflags & ~EBT_INV_MASK) {
++		BUGPRINT("Unknown flag for inv bitmask\n");
++		return -EINVAL;
++	}
++	if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++		BUGPRINT("NOPROTO & 802_3 not allowed\n");
++		return -EINVAL;
++	}
++	// what hook do we belong to?
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if ((char *)newinfo->hook_entry[i] < (char *)e)
++			hook = i;
++		else
++			break;
++	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
++	i = 0;
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++	if (ret != 0)
++		goto cleanup_matches;
++	j = 0;
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++	if (ret != 0)
++		goto cleanup_watchers;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target)
++		goto cleanup_watchers;
++	if (target->me)
++		__MOD_INC_USE_COUNT(target->me);
++	up(&ebt_mutex);
++
++	t->u.target = target;
++	if (t->u.target == &ebt_standard_target) {
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
++			BUGPRINT("Invalid standard target\n");
++			ret = -EFAULT;
++			goto cleanup_watchers;
++		}
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++		if (t->u.target->me)
++			__MOD_DEC_USE_COUNT(t->u.target->me);
++		ret = -EFAULT;
++		goto cleanup_watchers;
++	}
++	(*cnt)++;
++	return 0;
++cleanup_watchers:
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++	return ret;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++static int check_chainloops(struct ebt_entries *chain,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt, 
++   unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			// this can't be 0, so the above test is correct
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++   struct ebt_table_info *newinfo)
++{
++	unsigned int i, j, k, udc_cnt;
++	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++	i = 0;
++	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++		i++;
++	if (i == NF_BR_NUMHOOKS) {
++		BUGPRINT("No valid hooks specified\n");
++		return -EINVAL;
++	}
++	if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++		BUGPRINT("Chains don't start at beginning\n");
++		return -EINVAL;
++	}
++	// make sure chains are ordered after each other in same order
++	// as their corresponding hooks
++	for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++		if (!(repl->valid_hooks & (1 << j)))
++			continue;
++		if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++			BUGPRINT("Hook order must be followed\n");
++			return -EINVAL;
++		}
++		i = j;
++	}
++
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		newinfo->hook_entry[i] = NULL;
++
++	newinfo->entries_size = repl->entries_size;
++	newinfo->nentries = repl->nentries;
++
++	// do some early checkings and initialize some things
++	i = 0; // holds the expected nr. of entries for the chain
++	j = 0; // holds the up to now counted entries for the chain
++	k = 0; // holds the total nr. of entries, should equal
++	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++	   &udc_cnt, repl->valid_hooks);
++
++	if (ret != 0)
++		return ret;
++
++	if (i != j) {
++		BUGPRINT("nentries does not equal the nr of entries in the "
++		         "(last) chain\n");
++		return -EINVAL;
++	}
++	if (k != newinfo->nentries) {
++		BUGPRINT("Total nentries is wrong\n");
++		return -EINVAL;
++	}
++
++	// check if all valid hooks have a chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (newinfo->hook_entry[i] == NULL &&
++		   (repl->valid_hooks & (1 << i))) {
++			BUGPRINT("Valid hook without chain\n");
++			return -EINVAL;
++		}
++	}
++
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				return -ENOMEM;
++			}
++		}
++
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
++	// used to know what we need to clean up if something goes wrong
++	i = 0;
++	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
++	if (ret != 0) {
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
++	}
++	if (cl_s)
++		vfree(cl_s);
++	return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
++{
++	int i, cpu;
++	struct ebt_counter *counter_base;
++
++	// counters of cpu 0
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
++	// add other counters to those of cpu 0
++	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++		for (i = 0; i < nentries; i++) {
++			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
++	}
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++	int ret, i, countersize;
++	struct ebt_table_info *newinfo;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++	struct ebt_counter *counterstmp = NULL;
++	// used to be able to unlock earlier
++	struct ebt_table_info *table;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++		return -EFAULT;
++
++	if (len != sizeof(tmp) + tmp.entries_size) {
++		BUGPRINT("Wrong len argument\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size == 0) {
++		BUGPRINT("Entries_size never zero\n");
++		return -EINVAL;
++	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	if (!newinfo)
++		return -ENOMEM;
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	newinfo->entries = (char *)vmalloc(tmp.entries_size);
++	if (!newinfo->entries) {
++		ret = -ENOMEM;
++		goto free_newinfo;
++	}
++	if (copy_from_user(
++	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++		BUGPRINT("Couldn't copy entries from userspace\n");
++		ret = -EFAULT;
++		goto free_entries;
++	}
++
++	// the user wants counters back
++	// the check on the size is done later, when we have the lock
++	if (tmp.num_counters) {
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			ret = -ENOMEM;
++			goto free_entries;
++		}
++	}
++	else
++		counterstmp = NULL;
++
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
++	ret = translate_table(&tmp, newinfo);
++
++	if (ret != 0)
++		goto free_counterstmp;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_iterate;
++
++	// the table doesn't like it
++	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++		goto free_unlock;
++
++	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr. of counters requested\n");
++		ret = -EINVAL;
++		goto free_unlock;
++	}
++
++	// we have the mutex lock, so no danger in reading this pointer
++	table = t->private;
++	// we need an atomic snapshot of the counters
++	write_lock_bh(&t->lock);
++	if (tmp.num_counters)
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
++
++	t->private = newinfo;
++	write_unlock_bh(&t->lock);
++	up(&ebt_mutex);
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
++	// is held only once, while this doesn't bring the kernel into a
++	// dangerous state.
++	if (tmp.num_counters &&
++	   copy_to_user(tmp.counters, counterstmp,
++	   tmp.num_counters * sizeof(struct ebt_counter))) {
++		BUGPRINT("Couldn't copy counters to userspace\n");
++		ret = -EFAULT;
++	}
++	else
++		ret = 0;
++
++	// decrease module count and free resources
++	EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++	   ebt_cleanup_entry, NULL);
++
++	vfree(table->entries);
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
++		vfree(table->chainstack);
++	}
++	vfree(table);
++
++	if (counterstmp)
++		vfree(counterstmp);
++	return ret;
++
++free_unlock:
++	up(&ebt_mutex);
++free_iterate:
++	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++	   ebt_cleanup_entry, NULL);
++free_counterstmp:
++	if (counterstmp)
++		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++free_entries:
++	if (newinfo->entries)
++		vfree(newinfo->entries);
++free_newinfo:
++	if (newinfo)
++		vfree(newinfo);
++	return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_targets, target)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_targets, target);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_matches, match)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_matches, match);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++	int ret;
++
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		return ret;
++	if (!list_named_insert(&ebt_watchers, watcher)) {
++		up(&ebt_mutex);
++		return -EEXIST;
++	}
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++
++	return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_watchers, watcher);
++	up(&ebt_mutex);
++	MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++	struct ebt_table_info *newinfo;
++	int ret, i, countersize;
++
++	if (!table || !table->table ||!table->table->entries ||
++	    table->table->entries_size == 0 ||
++	    table->table->counters || table->private) {
++		BUGPRINT("Bad table data for ebt_register_table!!!\n");
++		return -EINVAL;
++	}
++
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++	newinfo = (struct ebt_table_info *)
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
++	ret = -ENOMEM;
++	if (!newinfo)
++		return -ENOMEM;
++
++	newinfo->entries = (char *)vmalloc(table->table->entries_size);
++	if (!(newinfo->entries))
++		goto free_newinfo;
++
++	memcpy(newinfo->entries, table->table->entries,
++	   table->table->entries_size);
++
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
++
++	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
++	ret = translate_table(table->table, newinfo);
++	if (ret != 0) {
++		BUGPRINT("Translate_table failed\n");
++		goto free_chainstack;
++	}
++
++	if (table->check && table->check(newinfo, table->valid_hooks)) {
++		BUGPRINT("The table doesn't like its own initial data, lol\n");
++		return -EINVAL;
++	}
++
++	table->private = newinfo;
++	table->lock = RW_LOCK_UNLOCKED;
++	ret = down_interruptible(&ebt_mutex);
++	if (ret != 0)
++		goto free_chainstack;
++
++	if (list_named_find(&ebt_tables, table->name)) {
++		ret = -EEXIST;
++		BUGPRINT("Table name already exists\n");
++		goto free_unlock;
++	}
++
++	list_prepend(&ebt_tables, table);
++	up(&ebt_mutex);
++	MOD_INC_USE_COUNT;
++	return 0;
++free_unlock:
++	up(&ebt_mutex);
++free_chainstack:
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
++		vfree(newinfo->chainstack);
++	}
++	vfree(newinfo->entries);
++free_newinfo:
++	vfree(newinfo);
++	return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++	int i;
++
++	if (!table) {
++		BUGPRINT("Request to unregister NULL table!!!\n");
++		return;
++	}
++	down(&ebt_mutex);
++	LIST_DELETE(&ebt_tables, table);
++	up(&ebt_mutex);
++	EBT_ENTRY_ITERATE(table->private->entries,
++	   table->private->entries_size, ebt_cleanup_entry, NULL);
++	if (table->private->entries)
++		vfree(table->private->entries);
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
++		vfree(table->private->chainstack);
++	}
++	vfree(table->private);
++	MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++	int i, ret;
++	struct ebt_counter *tmp;
++	struct ebt_replace hlp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&hlp, user, sizeof(hlp)))
++		return -EFAULT;
++
++	if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++		return -EINVAL;
++	if (hlp.num_counters == 0)
++		return -EINVAL;
++
++	if ( !(tmp = (struct ebt_counter *)
++	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++		MEMPRINT("Update_counters && nomemory\n");
++		return -ENOMEM;
++	}
++
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
++
++	if (hlp.num_counters != t->private->nentries) {
++		BUGPRINT("Wrong nr of counters\n");
++		ret = -EINVAL;
++		goto unlock_mutex;
++	}
++
++	if ( copy_from_user(tmp, hlp.counters,
++	   hlp.num_counters * sizeof(struct ebt_counter)) ) {
++		BUGPRINT("Updata_counters && !cfu\n");
++		ret = -EFAULT;
++		goto unlock_mutex;
++	}
++
++	// we want an atomic add of the counters
++	write_lock_bh(&t->lock);
++
++	// we add to the counters of the first cpu
++	for (i = 0; i < hlp.num_counters; i++) {
++		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
++
++	write_unlock_bh(&t->lock);
++	ret = 0;
++unlock_mutex:
++	up(&ebt_mutex);
++free_tmp:
++	vfree(tmp);
++	return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)m;
++	if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++   char *base, char *ubase)
++{
++	char *hlp = ubase - base + (char *)w;
++	if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++	int ret;
++	char *hlp;
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++
++	hlp = ubase - base + (char *)e + e->target_offset;
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	
++	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++	if (ret != 0)
++		return ret;
++	ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++	if (ret != 0)
++		return ret;
++	if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++		return -EFAULT;
++	return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
++{
++	struct ebt_replace tmp;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
++
++	if (copy_from_user(&tmp, user, sizeof(tmp))) {
++		BUGPRINT("Cfu didn't work\n");
++		return -EFAULT;
++	}
++
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	if (tmp.nentries != nentries) {
++		BUGPRINT("Nentries wrong\n");
++		return -EINVAL;
++	}
++
++	if (tmp.entries_size != entries_size) {
++		BUGPRINT("Wrong size\n");
++		return -EINVAL;
++	}
++
++	// userspace might not need the counters
++	if (tmp.num_counters) {
++		if (tmp.num_counters != nentries) {
++			BUGPRINT("Num_counters wrong\n");
++			return -EINVAL;
++		}
++		counterstmp = (struct ebt_counter *)
++		   vmalloc(nentries * sizeof(struct ebt_counter));
++		if (!counterstmp) {
++			MEMPRINT("Couldn't copy counters, out of memory\n");
++			return -ENOMEM;
++		}
++		write_lock_bh(&t->lock);
++		get_counters(oldcounters, counterstmp, nentries);
++		write_unlock_bh(&t->lock);
++
++		if (copy_to_user(tmp.counters, counterstmp,
++		   nentries * sizeof(struct ebt_counter))) {
++			BUGPRINT("Couldn't copy counters to userspace\n");
++			vfree(counterstmp);
++			return -EFAULT;
++		}
++		vfree(counterstmp);
++	}
++
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
++		BUGPRINT("Couldn't copy entries to userspace\n");
++		return -EFAULT;
++	}
++	// set the match/watcher/target names right
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++	int cmd, void *user, unsigned int len)
++{
++	int ret;
++
++	switch(cmd) {
++	case EBT_SO_SET_ENTRIES:
++		ret = do_replace(user, len);
++		break;
++	case EBT_SO_SET_COUNTERS:
++		ret = update_counters(user, len);
++		break;
++	default:
++		ret = -EINVAL;
++  }
++	return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++	int ret;
++	struct ebt_replace tmp;
++	struct ebt_table *t;
++
++	if (copy_from_user(&tmp, user, sizeof(tmp)))
++		return -EFAULT;
++
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
++		return ret;
++
++	switch(cmd) {
++	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
++		if (*len != sizeof(struct ebt_replace)){
++			ret = -EINVAL;
++			up(&ebt_mutex);
++			break;
++		}
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
++		up(&ebt_mutex);
++		if (copy_to_user(user, &tmp, *len) != 0){
++			BUGPRINT("c2u Didn't work\n");
++			ret = -EFAULT;
++			break;
++		}
++		ret = 0;
++		break;
++
++	case EBT_SO_GET_ENTRIES:
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
++		up(&ebt_mutex);
++		break;
++
++	default:
++		up(&ebt_mutex);
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++    EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++static int __init init(void)
++{
++	int ret;
++
++	down(&ebt_mutex);
++	list_named_insert(&ebt_targets, &ebt_standard_target);
++	up(&ebt_mutex);
++	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++		return ret;
++
++	printk(KERN_NOTICE "Ebtables v2.0 registered\n");
++	return 0;
++}
++
++static void __exit fini(void)
++{
++	nf_unregister_sockopt(&ebt_sockopts);
++	printk(KERN_NOTICE "Ebtables v2.0 unregistered\n");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebtables.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,361 @@
++/*
++ *  ebtables
++ *
++ *	Authors:
++ *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
++ *
++ *  ebtables.c,v 2.0, September, 2002
++ *
++ *  This code is stongly inspired on the iptables code which is
++ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	uint64_t pcnt;
++	uint64_t bcnt;
++};
++
++struct ebt_entries {
++	// this field is always set to zero
++	// See EBT_ENTRY_OR_ENTRIES.
++	// Must be same size as ebt_entry.bitmask
++	unsigned int distinguisher;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
++	// nr. of entries
++	unsigned int nentries;
++	// entry list
++	char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++   | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_match *match;
++	} u;
++	// size of data
++	unsigned int match_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_watcher *watcher;
++	} u;
++	// size of data
++	unsigned int watcher_size;
++	unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++	union {
++		char name[EBT_FUNCTION_MAXNAMELEN];
++		struct ebt_target *target;
++	} u;
++	// size of data
++	unsigned int target_size;
++	unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++	struct ebt_entry_target target;
++	int verdict;
++};
++
++// one entry
++struct ebt_entry {
++	// this needs to be the first field
++	unsigned int bitmask;
++	unsigned int invflags;
++	uint16_t ethproto;
++	// the physical in-dev
++	char in[IFNAMSIZ];
++	// the logical in-dev
++	char logical_in[IFNAMSIZ];
++	// the physical out-dev
++	char out[IFNAMSIZ];
++	// the logical out-dev
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
++	// sizeof ebt_entry + matches
++	unsigned int watchers_offset;
++	// sizeof ebt_entry + matches + watchers
++	unsigned int target_offset;
++	// sizeof ebt_entry + matches + watchers + target
++	unsigned int next_offset;
++	unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++	char name[EBT_TABLE_MAXNAMELEN];
++	unsigned int valid_hooks;
++	// nr of rules in the table
++	unsigned int nentries;
++	// total size of the entries
++	unsigned int entries_size;
++	// start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	char *entries;
++};
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
++struct ebt_match
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// 0 == it matches
++	int (*match)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *matchdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++	void (*destroy)(void *matchdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_watcher
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++	   const struct net_device *out, const void *watcherdata,
++	   unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++	void (*destroy)(void *watcherdata, unsigned int datalen);
++	struct module *me;
++};
++
++struct ebt_target
++{
++	struct list_head list;
++	const char name[EBT_FUNCTION_MAXNAMELEN];
++	// returns one of the standard verdicts
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
++	// 0 == let it in
++	int (*check)(const char *tablename, unsigned int hookmask,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++	void (*destroy)(void *targetdata, unsigned int datalen);
++	struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++	// total size of the entries
++	unsigned int entries_size;
++	unsigned int nentries;
++	// pointers to the start of the chains
++	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack **chainstack;
++	char *entries;
++	struct ebt_counter counters[0] ____cacheline_aligned;
++};
++
++struct ebt_table
++{
++	struct list_head list;
++	char name[EBT_TABLE_MAXNAMELEN];
++	struct ebt_replace *table;
++	unsigned int valid_hooks;
++	rwlock_t lock;
++	// e.g. could be the table explicitly only allows certain
++	// matches, targets, ... 0 == let it in
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
++	// the data used by the kernel
++	struct ebt_table_info *private;
++};
++
++#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_entry_target)-1)) & \
++		     ~(__alignof__(struct ebt_entry_target)-1))
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   struct ebt_table *table);
++
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...)                   \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_match *__match;                    \
++	                                                    \
++	for (__i = sizeof(struct ebt_entry);                \
++	     __i < (e)->watchers_offset;                    \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
++		__match = (void *)(e) + __i;                \
++		                                            \
++		__ret = fn(__match , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->watchers_offset)            \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry_watcher *__watcher;                \
++	                                                    \
++	for (__i = e->watchers_offset;                      \
++	     __i < (e)->target_offset;                      \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
++		__watcher = (void *)(e) + __i;              \
++		                                            \
++		__ret = fn(__watcher , ## args);            \
++		if (__ret != 0)                             \
++			break;                              \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (e)->target_offset)              \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
++({                                                          \
++	unsigned int __i;                                   \
++	int __ret = 0;                                      \
++	struct ebt_entry *__entry;                          \
++	                                                    \
++	for (__i = 0; __i < (size);) {                      \
++		__entry = (void *)(entries) + __i;          \
++		__ret = fn(__entry , ## args);              \
++		if (__ret != 0)                             \
++			break;                              \
++		if (__entry->bitmask != 0)                  \
++			__i += __entry->next_offset;        \
++		else                                        \
++			__i += sizeof(struct ebt_entries);  \
++	}                                                   \
++	if (__ret == 0) {                                   \
++		if (__i != (size))                          \
++			__ret = -EINVAL;                    \
++	}                                                   \
++	__ret;                                              \
++})
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arpreply.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
++#define __LINUX_BRIDGE_EBT_ARPREPLY_H
++
++struct ebt_arpreply_info
++{
++	unsigned char mac[ETH_ALEN];
++	int target;
++};
++#define EBT_ARPREPLY_TARGET "arpreply"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_802_3.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,60 @@
++#ifndef __LINUX_BRIDGE_EBT_802_3_H
++#define __LINUX_BRIDGE_EBT_802_3_H
++
++#define EBT_802_3_SAP 0x01
++#define EBT_802_3_TYPE 0x02
++
++#define EBT_802_3_MATCH "802_3"
++
++/*
++ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
++ * to discover what kind of packet we're carrying. 
++ */
++#define CHECK_TYPE 0xaa
++
++/*
++ * Control field may be one or two bytes.  If the first byte has
++ * the value 0x03 then the entire length is one byte, otherwise it is two.
++ * One byte controls are used in Unnumbered Information frames.
++ * Two byte controls are used in Numbered Information frames.
++ */
++#define IS_UI 0x03
++
++#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
++
++/* ui has one byte ctrl, ni has two */
++struct hdr_ui {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint8_t ctrl;
++	uint8_t orig[3];
++	uint16_t type;
++};
++
++struct hdr_ni {
++	uint8_t dsap;
++	uint8_t ssap;
++	uint16_t ctrl;
++	uint8_t  orig[3];
++	uint16_t type;
++};
++
++struct ebt_802_3_hdr {
++	uint8_t  daddr[6];
++	uint8_t  saddr[6];
++	uint16_t len;
++	union {
++		struct hdr_ui ui;
++		struct hdr_ni ni;
++	} llc;
++};
++
++struct ebt_802_3_info 
++{
++	uint8_t  sap;
++	uint16_t type;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_arp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,32 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_SRC_MAC 0x20
++#define EBT_ARP_DST_MAC 0x40
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	unsigned char smaddr[ETH_ALEN];
++	unsigned char smmsk[ETH_ALEN];
++	unsigned char dmaddr[ETH_ALEN];
++	unsigned char dmmsk[ETH_ALEN];
++	uint8_t  bitmask;
++	uint8_t  invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_ip.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,43 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_pkttype.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
++#define __LINUX_BRIDGE_EBT_PKTTYPE_H
++
++struct ebt_pkttype_info
++{
++	uint8_t pkt_type;
++	uint8_t invert;
++};
++#define EBT_PKTTYPE_MATCH "pkttype"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_stp.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,46 @@
++#ifndef __LINUX_BRIDGE_EBT_STP_H
++#define __LINUX_BRIDGE_EBT_STP_H
++
++#define EBT_STP_TYPE		0x0001
++
++#define EBT_STP_FLAGS		0x0002
++#define EBT_STP_ROOTPRIO	0x0004
++#define EBT_STP_ROOTADDR	0x0008
++#define EBT_STP_ROOTCOST	0x0010
++#define EBT_STP_SENDERPRIO	0x0020
++#define EBT_STP_SENDERADDR	0x0040
++#define EBT_STP_PORT		0x0080
++#define EBT_STP_MSGAGE		0x0100
++#define EBT_STP_MAXAGE		0x0200
++#define EBT_STP_HELLOTIME	0x0400
++#define EBT_STP_FWDD		0x0800
++
++#define EBT_STP_MASK		0x0fff
++#define EBT_STP_CONFIG_MASK	0x0ffe
++
++#define EBT_STP_MATCH "stp"
++
++struct ebt_stp_config_info
++{
++	uint8_t flags;
++	uint16_t root_priol, root_priou;
++	char root_addr[6], root_addrmsk[6];
++	uint32_t root_costl, root_costu;
++	uint16_t sender_priol, sender_priou;
++	char sender_addr[6], sender_addrmsk[6];
++	uint16_t portl, portu;
++	uint16_t msg_agel, msg_ageu;
++	uint16_t max_agel, max_ageu;
++	uint16_t hello_timel, hello_timeu;
++	uint16_t forward_delayl, forward_delayu;
++};
++
++struct ebt_stp_info
++{
++	uint8_t type;
++	struct ebt_stp_config_info config;
++	uint16_t bitmask;
++	uint16_t invflags;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_vlan.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_log.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
++};
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_nat.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_redirect.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_m.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	uint8_t invert;
++	uint8_t bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_bridge/ebt_mark_t.h	2003-09-03 21:08:30.000000000 +0200
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- linux-2.4.22/include/linux/netfilter.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter.h	2003-09-03 20:20:40.000000000 +0200
+@@ -118,17 +118,23 @@ extern struct list_head nf_hooks[NPROTO]
+ /* This is gross, but inline doesn't cut it for avoiding the function
+    call in fast path: gcc doesn't inline (needs value tracking?). --RR */
+ #ifdef CONFIG_NETFILTER_DEBUG
+-#define NF_HOOK nf_hook_slow
++#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
++nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
++#define NF_HOOK_THRESH nf_hook_slow
+ #else
+ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)			\
+ (list_empty(&nf_hooks[(pf)][(hook)])					\
+  ? (okfn)(skb)								\
+- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
++#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)	\
++(list_empty(&nf_hooks[(pf)][(hook)])					\
++ ? (okfn)(skb)								\
++ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
+ #endif
+ 
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev, struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *));
++		 int (*okfn)(struct sk_buff *), int thresh);
+ 
+ /* Call setsockopt() */
+ int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt, 
+--- linux-2.4.22/include/linux/netfilter_ipv4.h	2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4.h	2003-09-03 20:25:14.000000000 +0200
+@@ -52,8 +52,10 @@
+ enum nf_ip_hook_priorities {
+ 	NF_IP_PRI_FIRST = INT_MIN,
+ 	NF_IP_PRI_CONNTRACK = -200,
++	NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
+ 	NF_IP_PRI_MANGLE = -150,
+ 	NF_IP_PRI_NAT_DST = -100,
++	NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
+ 	NF_IP_PRI_FILTER = 0,
+ 	NF_IP_PRI_NAT_SRC = 100,
+ 	NF_IP_PRI_LAST = INT_MAX,
+--- linux-2.4.22/include/linux/skbuff.h	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/include/linux/skbuff.h	2003-09-03 20:26:39.000000000 +0200
+@@ -92,6 +92,17 @@ struct nf_conntrack {
+ struct nf_ct_info {
+ 	struct nf_conntrack *master;
+ };
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++struct nf_bridge_info {
++	atomic_t use;
++	struct net_device *physindev;
++	struct net_device *physoutdev;
++	unsigned int mask;
++	unsigned long hh[16 / sizeof(unsigned long)];
++};
++#endif
++
+ #endif
+ 
+ struct sk_buff_head {
+@@ -208,6 +219,9 @@ struct sk_buff {
+ #ifdef CONFIG_NETFILTER_DEBUG
+         unsigned int nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct nf_bridge_info	*nf_bridge;	/* Saved data about a bridged frame - see br_netfilter.c */
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ 
+ #if defined(CONFIG_HIPPI)
+@@ -1169,6 +1183,20 @@ nf_conntrack_get(struct nf_ct_info *nfct
+ 	if (nfct)
+ 		atomic_inc(&nfct->master->use);
+ }
++
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
++		kfree(nf_bridge);
++}
++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
++{
++	if (nf_bridge)
++		atomic_inc(&nf_bridge->use);
++}
++#endif
++
+ #endif
+ 
+ #endif	/* __KERNEL__ */
+--- linux-2.4.22/net/core/netfilter.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/netfilter.c	2003-09-03 21:39:40.000000000 +0200
+@@ -342,10 +342,15 @@ static unsigned int nf_iterate(struct li
+ 			       const struct net_device *indev,
+ 			       const struct net_device *outdev,
+ 			       struct list_head **i,
+-			       int (*okfn)(struct sk_buff *))
++			       int (*okfn)(struct sk_buff *),
++			       int hook_thresh)
+ {
+ 	for (*i = (*i)->next; *i != head; *i = (*i)->next) {
+ 		struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
++
++		if (hook_thresh > elem->priority)
++			continue;
++
+ 		switch (elem->hook(hook, skb, indev, outdev, okfn)) {
+ 		case NF_QUEUE:
+ 			return NF_QUEUE;
+@@ -413,6 +418,10 @@ static void nf_queue(struct sk_buff *skb
+ {
+ 	int status;
+ 	struct nf_info *info;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	struct net_device *physindev = NULL;
++	struct net_device *physoutdev = NULL;
++#endif
+ 
+ 	if (!queue_handler[pf].outfn) {
+ 		kfree_skb(skb);
+@@ -435,11 +444,24 @@ static void nf_queue(struct sk_buff *skb
+ 	if (indev) dev_hold(indev);
+ 	if (outdev) dev_hold(outdev);
+ 
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		physindev = skb->nf_bridge->physindev;
++		if (physindev) dev_hold(physindev);
++		physoutdev = skb->nf_bridge->physoutdev;
++		if (physoutdev) dev_hold(physoutdev);
++	}
++#endif
++
+ 	status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
+ 	if (status < 0) {
+ 		/* James M doesn't say fuck enough. */
+ 		if (indev) dev_put(indev);
+ 		if (outdev) dev_put(outdev);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		if (physindev) dev_put(physindev);
++		if (physoutdev) dev_put(physoutdev);
++#endif
+ 		kfree(info);
+ 		kfree_skb(skb);
+ 		return;
+@@ -449,7 +471,8 @@ static void nf_queue(struct sk_buff *skb
+ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
+ 		 struct net_device *indev,
+ 		 struct net_device *outdev,
+-		 int (*okfn)(struct sk_buff *))
++		 int (*okfn)(struct sk_buff *),
++		 int hook_thresh)
+ {
+ 	struct list_head *elem;
+ 	unsigned int verdict;
+@@ -481,7 +504,7 @@ int nf_hook_slow(int pf, unsigned int ho
+ 
+ 	elem = &nf_hooks[pf][hook];
+ 	verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
+-			     outdev, &elem, okfn);
++			     outdev, &elem, okfn, hook_thresh);
+ 	if (verdict == NF_QUEUE) {
+ 		NFDEBUG("nf_hook: Verdict = QUEUE.\n");
+ 		nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
+@@ -510,6 +533,14 @@ void nf_reinject(struct sk_buff *skb, st
+ 
+ 	/* We don't have BR_NETPROTO_LOCK here */
+ 	br_read_lock_bh(BR_NETPROTO_LOCK);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if (skb->nf_bridge) {
++		if (skb->nf_bridge->physindev)
++			dev_put(skb->nf_bridge->physindev);
++		if (skb->nf_bridge->physoutdev)
++			dev_put(skb->nf_bridge->physoutdev);
++	}
++#endif
+ 	for (i = nf_hooks[info->pf][info->hook].next; i != elem; i = i->next) {
+ 		if (i == &nf_hooks[info->pf][info->hook]) {
+ 			/* The module which sent it to userspace is gone. */
+@@ -530,7 +561,7 @@ void nf_reinject(struct sk_buff *skb, st
+ 		verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
+ 				     &skb, info->hook, 
+ 				     info->indev, info->outdev, &elem,
+-				     info->okfn);
++				     info->okfn, INT_MIN);
+ 	}
+ 
+ 	switch (verdict) {
+--- linux-2.4.22/net/core/skbuff.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/core/skbuff.c	2003-09-03 20:31:51.000000000 +0200
+@@ -246,6 +246,9 @@ static inline void skb_headerinit(void *
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	skb->nf_debug = 0;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	skb->nf_bridge	  = NULL;
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	skb->tc_index = 0;
+@@ -326,6 +329,9 @@ void __kfree_skb(struct sk_buff *skb)
+ 	}
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_put(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_put(skb->nf_bridge);
++#endif
+ #endif
+ 	skb_headerinit(skb, NULL, 0);  /* clean state */
+ 	kfree_skbmem(skb);
+@@ -393,6 +399,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	C(nf_debug);
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	C(nf_bridge);
++#endif
+ #endif /*CONFIG_NETFILTER*/
+ #if defined(CONFIG_HIPPI)
+ 	C(private);
+@@ -405,6 +414,9 @@ struct sk_buff *skb_clone(struct sk_buff
+ 	skb->cloned = 1;
+ #ifdef CONFIG_NETFILTER
+ 	nf_conntrack_get(skb->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	nf_bridge_get(skb->nf_bridge);
++#endif
+ #endif
+ 	return n;
+ }
+@@ -440,6 +452,10 @@ static void copy_skb_header(struct sk_bu
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 	new->nf_debug=old->nf_debug;
+ #endif
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	new->nf_bridge=old->nf_bridge;
++	nf_bridge_get(new->nf_bridge);
++#endif
+ #endif
+ #ifdef CONFIG_NET_SCHED
+ 	new->tc_index = old->tc_index;
+--- linux-2.4.22/net/ipv4/netfilter/ip_tables.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ip_tables.c	2003-09-03 20:36:13.000000000 +0200
+@@ -121,12 +121,19 @@ static LIST_HEAD(ipt_tables);
+ static inline int
+ ip_packet_match(const struct iphdr *ip,
+ 		const char *indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physindev,
++#endif
+ 		const char *outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		const char *physoutdev,
++#endif
+ 		const struct ipt_ip *ipinfo,
+ 		int isfrag)
+ {
+ 	size_t i;
+ 	unsigned long ret;
++	unsigned long ret2 = 1;
+ 
+ #define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+ 
+@@ -156,7 +163,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->iniface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physindev)[i]
++			^ ((const unsigned long *)ipinfo->iniface)[i])
++			& ((const unsigned long *)ipinfo->iniface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
+ 		dprintf("VIA in mismatch (%s vs %s).%s\n",
+ 			indev, ipinfo->iniface,
+ 			ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+@@ -169,7 +184,15 @@ ip_packet_match(const struct iphdr *ip,
+ 			& ((const unsigned long *)ipinfo->outiface_mask)[i];
+ 	}
+ 
+-	if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret2 |= (((const unsigned long *)physoutdev)[i]
++			^ ((const unsigned long *)ipinfo->outiface)[i])
++			& ((const unsigned long *)ipinfo->outiface_mask)[i];
++	}
++#endif
++
++	if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
+ 		dprintf("VIA out mismatch (%s vs %s).%s\n",
+ 			outdev, ipinfo->outiface,
+ 			ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+@@ -268,6 +291,9 @@ ipt_do_table(struct sk_buff **pskb,
+ 	/* Initializing verdict to NF_DROP keeps gcc happy. */
+ 	unsigned int verdict = NF_DROP;
+ 	const char *indev, *outdev;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	const char *physindev, *physoutdev;
++#endif
+ 	void *table_base;
+ 	struct ipt_entry *e, *back;
+ 
+@@ -277,6 +303,13 @@ ipt_do_table(struct sk_buff **pskb,
+ 	datalen = (*pskb)->len - ip->ihl * 4;
+ 	indev = in ? in->name : nulldevname;
+ 	outdev = out ? out->name : nulldevname;
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	physindev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physindev) ?
++		(*pskb)->nf_bridge->physindev->name : nulldevname;
++	physoutdev = ((*pskb)->nf_bridge && (*pskb)->nf_bridge->physoutdev) ?
++		(*pskb)->nf_bridge->physoutdev->name : nulldevname;
++#endif
++
+ 	/* We handle fragments by dealing with the first fragment as
+ 	 * if it was a normal packet.  All other fragments are treated
+ 	 * normally, except that they will NEVER match rules that ask
+@@ -312,7 +345,15 @@ ipt_do_table(struct sk_buff **pskb,
+ 		IP_NF_ASSERT(e);
+ 		IP_NF_ASSERT(back);
+ 		(*pskb)->nfcache |= e->nfcache;
+-		if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
++		if (ip_packet_match(ip, indev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physindev,
++#endif
++		    outdev,
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		    physoutdev,
++#endif
++		    &e->ip, offset)) {
+ 			struct ipt_entry_target *t;
+ 
+ 			if (IPT_MATCH_ITERATE(e, do_match,
+--- linux-2.4.22/net/ipv4/ip_output.c	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/ip_output.c	2003-09-03 20:36:57.000000000 +0200
+@@ -882,6 +882,10 @@ int ip_fragment(struct sk_buff *skb, int
+ 		/* Connection association is same as pre-frag packet */
+ 		skb2->nfct = skb->nfct;
+ 		nf_conntrack_get(skb2->nfct);
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++		skb2->nf_bridge = skb->nf_bridge;
++		nf_bridge_get(skb2->nf_bridge);
++#endif
+ #ifdef CONFIG_NETFILTER_DEBUG
+ 		skb2->nf_debug = skb->nf_debug;
+ #endif
+--- linux-2.4.22/net/ipv4/netfilter/ipt_LOG.c	2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_LOG.c	2003-09-03 20:37:40.000000000 +0200
+@@ -289,6 +289,18 @@ ipt_log_target(struct sk_buff **pskb,
+ 	       loginfo->prefix,
+ 	       in ? in->name : "",
+ 	       out ? out->name : "");
++#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
++	if ((*pskb)->nf_bridge) {
++		struct net_device *physindev = (*pskb)->nf_bridge->physindev;
++		struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
++
++		if (physindev && in != physindev)
++			printk("PHYSIN=%s ", physindev->name);
++		if (physoutdev && out != physoutdev)
++			printk("PHYSOUT=%s ", physoutdev->name);
++	}
++#endif
++
+ 	if (in && !out) {
+ 		/* MAC logging for input chain only. */
+ 		printk("MAC=");
+--- linux-2.4.22/net/ipv4/netfilter/Makefile	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Makefile	2003-09-03 20:38:15.000000000 +0200
+@@ -87,6 +87,8 @@ obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += i
+ obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o
+ obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o
+ 
++obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_MIRROR) += ipt_MIRROR.o
+--- linux-2.4.22/net/ipv4/netfilter/Config.in	2003-08-25 13:44:44.000000000 +0200
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/Config.in	2003-09-03 21:40:55.000000000 +0200
+@@ -44,6 +44,9 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; 
+     dep_tristate '  Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+     dep_tristate '  Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+   fi
++  if [ "$CONFIG_BRIDGE" != "n" ]; then
++    dep_tristate '  Physdev match support' CONFIG_IP_NF_MATCH_PHYSDEV $CONFIG_IP_NF_IPTABLES
++  fi
+ # The targets
+   dep_tristate '  Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES 
+   if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/bridge/br_netfilter.c	2003-09-03 21:14:20.000000000 +0200
+@@ -0,0 +1,636 @@
++/*
++ *	Handle firewalling
++ *	Linux ethernet bridge
++ *
++ *	Authors:
++ *	Lennert Buytenhek               <buytenh@gnu.org>
++ *	Bart De Schuymer		<bdschuym@pandora.be>
++ *
++ *	This program is free software; you can redistribute it and/or
++ *	modify it under the terms of the GNU General Public License
++ *	as published by the Free Software Foundation; either version
++ *	2 of the License, or (at your option) any later version.
++ *
++ *	Lennert dedicates this file to Kerstin Wurdinger.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ip.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/in_route.h>
++#include <net/ip.h>
++#include <asm/uaccess.h>
++#include <asm/checksum.h>
++#include "br_private.h"
++
++
++#define skb_origaddr(skb)	 (((struct bridge_skb_cb *) \
++				 (skb->cb))->daddr.ipv4)
++#define store_orig_dstaddr(skb)	 (skb_origaddr(skb) = (skb)->nh.iph->daddr)
++#define dnat_took_place(skb)	 (skb_origaddr(skb) != (skb)->nh.iph->daddr)
++#define clear_cb(skb)		 (memset(&skb_origaddr(skb), 0, \
++				 sizeof(struct bridge_skb_cb)))
++
++#define has_bridge_parent(device)	((device)->br_port != NULL)
++#define bridge_parent(device)		(&((device)->br_port->br->dev))
++
++/* We need these fake structures to make netfilter happy --
++ * lots of places assume that skb->dst != NULL, which isn't
++ * all that unreasonable.
++ *
++ * Currently, we fill in the PMTU entry because netfilter
++ * refragmentation needs it, and the rt_flags entry because
++ * ipt_REJECT needs it.  Future netfilter modules might
++ * require us to fill additional fields.
++ */
++static struct net_device __fake_net_device = {
++	.hard_header_len	= ETH_HLEN
++};
++
++static struct rtable __fake_rtable = {
++	u: {
++		dst: {
++			__refcnt:		ATOMIC_INIT(1),
++			dev:			&__fake_net_device,
++			pmtu:			1500
++		}
++	},
++
++	rt_flags:	0
++};
++
++
++/* PF_BRIDGE/PRE_ROUTING *********************************************/
++static void __br_dnat_complain(void)
++{
++	static unsigned long last_complaint = 0;
++
++	if (jiffies - last_complaint >= 5 * HZ) {
++		printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
++			"forwarding to be enabled\n");
++		last_complaint = jiffies;
++	}
++}
++
++
++/* This requires some explaining. If DNAT has taken place,
++ * we will need to fix up the destination Ethernet address,
++ * and this is a tricky process.
++ *
++ * There are two cases to consider:
++ * 1. The packet was DNAT'ed to a device in the same bridge
++ *    port group as it was received on. We can still bridge
++ *    the packet.
++ * 2. The packet was DNAT'ed to a different device, either
++ *    a non-bridged device or another bridge port group.
++ *    The packet will need to be routed.
++ *
++ * The correct way of distinguishing between these two cases is to
++ * call ip_route_input() and to look at skb->dst->dev, which is
++ * changed to the destination device if ip_route_input() succeeds.
++ *
++ * Let us first consider the case that ip_route_input() succeeds:
++ *
++ * If skb->dst->dev equals the logical bridge device the packet
++ * came in on, we can consider this bridging. We then call
++ * skb->dst->output() which will make the packet enter br_nf_local_out()
++ * not much later. In that function it is assured that the iptables
++ * FORWARD chain is traversed for the packet.
++ *
++ * Otherwise, the packet is considered to be routed and we just
++ * change the destination MAC address so that the packet will
++ * later be passed up to the IP stack to be routed.
++ *
++ * Let us now consider the case that ip_route_input() fails:
++ *
++ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
++ * will fail, while __ip_route_output_key() will return success. The source
++ * address for __ip_route_output_key() is set to zero, so __ip_route_output_key
++ * thinks we're handling a locally generated packet and won't care
++ * if IP forwarding is allowed. We send a warning message to the users's
++ * log telling her to put IP forwarding on.
++ *
++ * ip_route_input() will also fail if there is no route available.
++ * In that case we just drop the packet.
++ *
++ * --Lennert, 20020411
++ * --Bart, 20020416 (updated)
++ * --Bart, 20021007 (updated)
++ */
++
++static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
++#endif
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		skb->nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	skb->dev = bridge_parent(skb->dev);
++	skb->dst->output(skb);
++	return 0;
++}
++
++static int br_nf_pre_routing_finish(struct sk_buff *skb)
++{
++	struct net_device *dev = skb->dev;
++	struct iphdr *iph = skb->nh.iph;
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	if (dnat_took_place(skb)) {
++		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
++		    dev)) {
++			struct rtable *rt;
++
++			if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
++				/* Bridged-and-DNAT'ed traffic doesn't
++				 * require ip_forwarding.
++				 */
++				if (((struct dst_entry *)rt)->dev == dev) {
++					skb->dst = (struct dst_entry *)rt;
++					goto bridged_dnat;
++				}
++				__br_dnat_complain();
++				dst_release((struct dst_entry *)rt);
++			}
++			kfree_skb(skb);
++			return 0;
++		} else {
++			if (skb->dst->dev == dev) {
++bridged_dnat:
++				/* Tell br_nf_local_out this is a
++				 * bridged frame
++				 */
++				nf_bridge->mask |= BRNF_BRIDGED_DNAT;
++				skb->dev = nf_bridge->physindev;
++				clear_cb(skb);
++				NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
++					       skb, skb->dev, NULL,
++					       br_nf_pre_routing_finish_bridge,
++					       1);
++				return 0;
++			}
++			memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
++			       ETH_ALEN);
++		}
++	} else {
++		skb->dst = (struct dst_entry *)&__fake_rtable;
++		dst_hold(skb->dst);
++	}
++
++	clear_cb(skb);
++	skb->dev = nf_bridge->physindev;
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
++		       br_handle_frame_finish, 1);
++
++	return 0;
++}
++
++/* Replicate the checks that IPv4 does on packet reception.
++ * Set skb->dev to the bridge device (i.e. parent of the
++ * receiving device) to make netfilter happy, the REDIRECT
++ * target in particular.  Save the original destination IP
++ * address to be able to detect DNAT afterwards.
++ */
++static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct iphdr *iph;
++	__u32 len;
++	struct sk_buff *skb;
++	struct nf_bridge_info *nf_bridge;
++
++	if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
++		goto out;
++
++	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (iph->ihl < 5 || iph->version != 4)
++		goto inhdr_error;
++
++	if (!pskb_may_pull(skb, 4*iph->ihl))
++		goto inhdr_error;
++
++	iph = skb->nh.iph;
++	if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
++		goto inhdr_error;
++
++	len = ntohs(iph->tot_len);
++	if (skb->len < len || len < 4*iph->ihl)
++		goto inhdr_error;
++
++	if (skb->len > len) {
++		__pskb_trim(skb, len);
++		if (skb->ip_summed == CHECKSUM_HW)
++			skb->ip_summed = CHECKSUM_NONE;
++	}
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
++#endif
++ 	if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
++		return NF_DROP;
++
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->physindev = skb->dev;
++	skb->dev = bridge_parent(skb->dev);
++	store_orig_dstaddr(skb);
++
++	NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
++		br_nf_pre_routing_finish);
++
++	return NF_STOLEN;
++
++inhdr_error:
++//	IP_INC_STATS_BH(IpInHdrErrors);
++out:
++	return NF_DROP;
++}
++
++
++/* PF_BRIDGE/LOCAL_IN ************************************************/
++/* The packet is locally destined, which requires a real
++ * dst_entry, so detach the fake one.  On the way up, the
++ * packet would pass through PRE_ROUTING again (which already
++ * took place when the packet entered the bridge), but we
++ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
++ * prevent this from happening.
++ */
++static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	if (skb->dst == (struct dst_entry *)&__fake_rtable) {
++		dst_release(skb->dst);
++		skb->dst = NULL;
++	}
++
++	return NF_ACCEPT;
++}
++
++
++/* PF_BRIDGE/FORWARD *************************************************/
++static int br_nf_forward_finish(struct sk_buff *skb)
++{
++	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	if (nf_bridge->mask & BRNF_PKT_TYPE) {
++		skb->pkt_type = PACKET_OTHERHOST;
++		nf_bridge->mask ^= BRNF_PKT_TYPE;
++	}
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
++			skb->dev, br_forward_finish, 1);
++
++	return 0;
++}
++
++/* This is the 'purely bridged' case.  We pass the packet to
++ * netfilter with indev and outdev set to the bridge device,
++ * but we are still able to filter on the 'real' indev/outdev
++ * because of the ipt_physdev.c module.
++ */
++static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_BR_FORWARD);
++#endif
++
++	nf_bridge = skb->nf_bridge;
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	nf_bridge->mask |= BRNF_BRIDGED; /* The physdev module checks on this */
++	nf_bridge->physoutdev = skb->dev;
++
++	NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
++			bridge_parent(skb->dev), br_nf_forward_finish);
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/LOCAL_OUT ***********************************************/
++static int br_nf_local_out_finish(struct sk_buff *skb)
++{
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
++#endif
++
++	NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
++			br_forward_finish, NF_BR_PRI_FIRST + 1);
++
++	return 0;
++}
++
++
++/* This function sees both locally originated IP packets and forwarded
++ * IP packets (in both cases the destination device is a bridge
++ * device). It also sees bridged-and-DNAT'ed packets.
++ * To be able to filter on the physical bridge devices (with the ipt_physdev.c
++ * module), we steal packets destined to a bridge device away from the
++ * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later,
++ * when we have determined the real output device. This is done in here.
++ *
++ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
++ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
++ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
++ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
++ * will be executed.
++ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
++ * this packet before, and so the packet was locally originated. We fake
++ * the PF_INET/LOCAL_OUT hook.
++ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
++ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
++ * even routed packets that didn't arrive on a bridge interface have their
++ * nf_bridge->physindev set.
++ */
++
++static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*_okfn)(struct sk_buff *))
++{
++	int (*okfn)(struct sk_buff *skb);
++	struct net_device *realindev;
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge;
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++	nf_bridge = skb->nf_bridge;
++	nf_bridge->physoutdev = skb->dev;
++
++	realindev = nf_bridge->physindev;
++
++	/* Bridged, take PF_BRIDGE/FORWARD.
++	 * (see big note in front of br_nf_pre_routing_finish)
++	 */
++	if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
++		okfn = br_forward_finish;
++
++		if (nf_bridge->mask & BRNF_PKT_TYPE) {
++			skb->pkt_type = PACKET_OTHERHOST;
++			nf_bridge->mask ^= BRNF_PKT_TYPE;
++		}
++
++		NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
++			skb->dev, okfn);
++	} else {
++		okfn = br_nf_local_out_finish;
++		/* IP forwarded traffic has a physindev, locally
++		 * generated traffic hasn't.
++		 */
++		if (realindev != NULL) {
++			if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
++			    has_bridge_parent(realindev))
++				realindev = bridge_parent(realindev);
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
++		} else {
++#ifdef CONFIG_NETFILTER_DEBUG
++			skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
++#endif
++
++			NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
++				       bridge_parent(skb->dev), okfn,
++				       NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
++		}
++	}
++
++	return NF_STOLEN;
++}
++
++
++/* PF_BRIDGE/POST_ROUTING ********************************************/
++static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	struct sk_buff *skb = *pskb;
++	struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
++
++	/* Be very paranoid. Must be a device driver bug. */
++	if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
++		printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
++				 "bad mac.raw pointer.");
++		if (skb->dev != NULL) {
++			printk("[%s]", skb->dev->name);
++			if (has_bridge_parent(skb->dev))
++				printk("[%s]", bridge_parent(skb->dev)->name);
++		}
++		printk("\n");
++		return NF_ACCEPT;
++	}
++
++	if (skb->protocol != __constant_htons(ETH_P_IP))
++		return NF_ACCEPT;
++
++	/* Sometimes we get packets with NULL ->dst here (for example,
++	 * running a dhcp client daemon triggers this).
++	 */
++	if (skb->dst == NULL)
++		return NF_ACCEPT;
++
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
++#endif
++
++	/* We assume any code from br_dev_queue_push_xmit onwards doesn't care
++	 * about the value of skb->pkt_type.
++	 */
++	if (skb->pkt_type == PACKET_OTHERHOST) {
++		skb->pkt_type = PACKET_HOST;
++		nf_bridge->mask |= BRNF_PKT_TYPE;
++	}
++
++	memcpy(nf_bridge->hh, skb->data - 16, 16);
++
++	NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
++		bridge_parent(skb->dev), br_dev_queue_push_xmit);
++
++	return NF_STOLEN;
++}
++
++
++/* IPv4/SABOTAGE *****************************************************/
++
++/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
++ * for the second time.
++ */
++static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (in->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_pre_routing_finish) {
++		okfn(*pskb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
++ * and PF_INET/POST_ROUTING until we have done the forwarding
++ * decision in the bridge code and have determined skb->physoutdev.
++ */
++static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
++{
++	if (out->hard_start_xmit == br_dev_xmit &&
++	    okfn != br_nf_forward_finish &&
++	    okfn != br_nf_local_out_finish &&
++	    okfn != br_dev_queue_push_xmit) {
++		struct sk_buff *skb = *pskb;
++		struct nf_bridge_info *nf_bridge;
++
++		if (!skb->nf_bridge && !nf_bridge_alloc(skb))
++			return NF_DROP;
++
++		nf_bridge = skb->nf_bridge;
++
++		/* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
++		 * will need the indev then. For a brouter, the real indev
++		 * can be a bridge port, so we make sure br_nf_local_out()
++		 * doesn't use the bridge parent of the indev by using
++		 * the BRNF_DONT_TAKE_PARENT mask.
++		 */
++		if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
++			nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
++			nf_bridge->physindev = (struct net_device *)in;
++		}
++		okfn(skb);
++		return NF_STOLEN;
++	}
++
++	return NF_ACCEPT;
++}
++
++/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
++ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
++ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
++ * ip_refrag() can return NF_STOLEN.
++ */
++static struct nf_hook_ops br_nf_ops[] = {
++	{ .hook = br_nf_pre_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_PRE_ROUTING,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_in,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_IN,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_forward,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_FORWARD,
++	  .priority = NF_BR_PRI_BRNF, },
++	{ .hook = br_nf_local_out,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_LOCAL_OUT,
++	  .priority = NF_BR_PRI_FIRST, },
++	{ .hook = br_nf_post_routing,
++	  .pf = PF_BRIDGE,
++	  .hooknum = NF_BR_POST_ROUTING,
++	  .priority = NF_BR_PRI_LAST, },
++	{ .hook = ipv4_sabotage_in,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_PRE_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_FORWARD,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_LOCAL_OUT,
++	  .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, },
++	{ .hook = ipv4_sabotage_out,
++	  .pf = PF_INET,
++	  .hooknum = NF_IP_POST_ROUTING,
++	  .priority = NF_IP_PRI_FIRST, },
++};
++
++int br_netfilter_init(void)
++{
++	int i;
++
++	for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++) {
++		int ret;
++
++		if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
++			continue;
++
++		while (i--)
++			nf_unregister_hook(&br_nf_ops[i]);
++
++		return ret;
++	}
++
++	printk(KERN_NOTICE "Bridge firewalling registered\n");
++
++	return 0;
++}
++
++void br_netfilter_fini(void)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(br_nf_ops) - 1; i >= 0; i--)
++		nf_unregister_hook(&br_nf_ops[i]);
++}
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/net/ipv4/netfilter/ipt_physdev.c	2003-09-03 21:15:28.000000000 +0200
+@@ -0,0 +1,127 @@
++/* Kernel module to match the bridge port in and
++ * out device for IP packets coming into contact with a bridge. */
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ipt_physdev.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#define MATCH   1
++#define NOMATCH 0
++
++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)
++{
++	int i;
++	static const char nulldevname[IFNAMSIZ] = { 0 };
++	const struct ipt_physdev_info *info = matchinfo;
++	unsigned long ret;
++	const char *indev, *outdev;
++	struct nf_bridge_info *nf_bridge;
++
++	/* Not a bridged IP packet or no info available yet:
++	 * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
++	 * the destination device will be a bridge. */
++	if (!(nf_bridge = skb->nf_bridge)) {
++		/* Return MATCH if the invert flags of the used options are on */
++		if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++		    !(info->invert & IPT_PHYSDEV_OP_BRIDGED))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISIN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISIN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_ISOUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_ISOUT))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_IN) &&
++		    !(info->invert & IPT_PHYSDEV_OP_IN))
++			return NOMATCH;
++		if ((info->bitmask & IPT_PHYSDEV_OP_OUT) &&
++		    !(info->invert & IPT_PHYSDEV_OP_OUT))
++			return NOMATCH;
++		return MATCH;
++	}
++
++	/* This only makes sense in the FORWARD and POSTROUTING chains */
++	if ((info->bitmask & IPT_PHYSDEV_OP_BRIDGED) &&
++	    (!!(nf_bridge->mask & BRNF_BRIDGED) ^
++	    !(info->invert & IPT_PHYSDEV_OP_BRIDGED)))
++		return NOMATCH;
++
++	if ((info->bitmask & IPT_PHYSDEV_OP_ISIN &&
++	    (!nf_bridge->physindev ^ !!(info->invert & IPT_PHYSDEV_OP_ISIN))) ||
++	    (info->bitmask & IPT_PHYSDEV_OP_ISOUT &&
++	    (!nf_bridge->physoutdev ^ !!(info->invert & IPT_PHYSDEV_OP_ISOUT))))
++		return NOMATCH;
++
++	if (!(info->bitmask & IPT_PHYSDEV_OP_IN))
++		goto match_outdev;
++	indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)indev)[i]
++			^ ((const unsigned long *)info->physindev)[i])
++			& ((const unsigned long *)info->in_mask)[i];
++	}
++
++	if ((ret == 0) ^ !(info->invert & IPT_PHYSDEV_OP_IN))
++		return NOMATCH;
++
++match_outdev:
++	if (!(info->bitmask & IPT_PHYSDEV_OP_OUT))
++		return MATCH;
++	outdev = nf_bridge->physoutdev ?
++		 nf_bridge->physoutdev->name : nulldevname;
++	for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
++		ret |= (((const unsigned long *)outdev)[i]
++			^ ((const unsigned long *)info->physoutdev)[i])
++			& ((const unsigned long *)info->out_mask)[i];
++	}
++
++	return (ret != 0) ^ !(info->invert & IPT_PHYSDEV_OP_OUT);
++}
++
++static int
++checkentry(const char *tablename,
++		       const struct ipt_ip *ip,
++		       void *matchinfo,
++		       unsigned int matchsize,
++		       unsigned int hook_mask)
++{
++	const struct ipt_physdev_info *info = matchinfo;
++
++	if (matchsize != IPT_ALIGN(sizeof(struct ipt_physdev_info)))
++		return 0;
++	if (!(info->bitmask & IPT_PHYSDEV_OP_MASK) ||
++	    info->bitmask & ~IPT_PHYSDEV_OP_MASK)
++		return 0;
++	return 1;
++}
++
++static struct ipt_match physdev_match = {
++	.name		= "physdev",
++	.match		= &match,
++	.checkentry	= &checkentry,
++	.me		= THIS_MODULE,
++};
++
++static int __init init(void)
++{
++	return ipt_register_match(&physdev_match);
++}
++
++static void __exit fini(void)
++{
++	ipt_unregister_match(&physdev_match);
++}
++
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.22-ebt-brnf/include/linux/netfilter_ipv4/ipt_physdev.h	2003-09-03 21:15:59.000000000 +0200
+@@ -0,0 +1,24 @@
++#ifndef _IPT_PHYSDEV_H
++#define _IPT_PHYSDEV_H
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#endif
++
++#define IPT_PHYSDEV_OP_IN		0x01
++#define IPT_PHYSDEV_OP_OUT		0x02
++#define IPT_PHYSDEV_OP_BRIDGED		0x04
++#define IPT_PHYSDEV_OP_ISIN		0x08
++#define IPT_PHYSDEV_OP_ISOUT		0x10
++#define IPT_PHYSDEV_OP_MASK		(0x20 - 1)
++
++struct ipt_physdev_info {
++	u_int8_t invert;
++	u_int8_t bitmask;
++	char physindev[IFNAMSIZ];
++	char in_mask[IFNAMSIZ];
++	char physoutdev[IFNAMSIZ];
++	char out_mask[IFNAMSIZ];
++};
++
++#endif /*_IPT_PHYSDEV_H*/
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff
new file mode 100644
index 0000000..6e03a05
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff
@@ -0,0 +1,1152 @@
+--- linux/net/core/dev.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/core/dev.c	Wed Jul 31 19:04:30 2002
+@@ -1385,13 +1385,6 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *)) = NULL;
+-#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+--- linux/net/bridge/br.c	Wed Jul 31 19:10:53 2002
++++ ebt2.0-rc1/net/bridge/br.c	Wed Jul 31 19:04:30 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++                        const struct net_device *in,
++                        const struct net_device *out,
++                        int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ 	MOD_DEC_USE_COUNT;
+@@ -82,7 +90,12 @@
+ #endif
+ }
+ 
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#else
+ EXPORT_NO_SYMBOLS;
++#endif
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux/net/bridge/Makefile	Wed Jul 31 19:10:53 2002
++++ ebt2.0-rc1/net/bridge/Makefile	Wed Jul 31 19:04:30 2002
+@@ -7,6 +7,12 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ 			br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux/include/linux/netfilter_bridge.h	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/include/linux/netfilter_bridge.h	Wed Jul 31 19:04:30 2002
+@@ -28,8 +28,7 @@
+         NF_BR_PRI_FILTER_OTHER = 200,
+         NF_BR_PRI_NAT_DST_BRIDGED = -300,
+         NF_BR_PRI_NAT_DST_OTHER = 100,
+-        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
+-        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_NAT_SRC = 300,
+         NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
+--- linux/net/netsyms.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/netsyms.c	Mon Feb 25 20:38:14 2002
+@@ -228,10 +228,6 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-EXPORT_SYMBOL(broute_decision);
+-#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/net/Makefile	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/Makefile	Wed Jul 31 19:04:30 2002
+@@ -28,7 +28,7 @@
+ endif
+ 
+ ifneq ($(CONFIG_BRIDGE),n)
+-ifneq ($CONFIG_BRIDGE),)
++ifneq ($(CONFIG_BRIDGE),)
+ subdir-$(CONFIG_BRIDGE)		+= bridge/netfilter
+ endif
+ endif
+--- linux/net/bridge/netfilter/Makefile	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/Makefile	Wed Jul 31 19:04:30 2002
+@@ -9,7 +9,7 @@
+ 
+ O_TARGET	:= netfilter.o
+ 
+-export-objs = ebtables.o
++export-objs := ebtables.o
+ 
+ obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+@@ -19,8 +19,10 @@
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
+ obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
+ obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+ obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+ include $(TOPDIR)/Rules.make
+--- linux/net/bridge/netfilter/Config.in	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/Config.in	Wed Jul 31 19:04:30 2002
+@@ -9,8 +9,10 @@
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
+ dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+ 
+--- linux/net/bridge/netfilter/ebtable_nat.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_nat.c	Wed Jul 31 19:04:30 2002
+@@ -42,16 +42,6 @@
+   RW_LOCK_UNLOCKED, check, NULL
+ };
+ 
+-// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
+-// needed because of the bridge-nf patch (that allows use of iptables
+-// on bridged traffic)
+-// if the packet is routed, we want the ebtables stuff on POSTROUTING
+-// to be executed _after_ the iptables stuff. when it's bridged, it's
+-// the way around
+-static struct net_device __fake_net_device = {
+-        hard_header_len:        ETH_HLEN
+-};
+-
+ static unsigned int
+ ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
+    const struct net_device *in, const struct net_device *out,
+@@ -60,50 +50,11 @@
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+ 
+-// let snat know this frame is routed
+-static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *))
+-{
+-	(*pskb)->physindev = NULL;
+-	return NF_ACCEPT;
+-}
+-
+-// let snat know this frame is bridged
+-static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *))
+-{
+-	(*pskb)->physindev = &__fake_net_device;
+-	return NF_ACCEPT;
+-}
+-
+ static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
+ 			const struct net_device *in,
+ 			const struct net_device *out,
+ 			int (*okfn)(struct sk_buff *))
+ {
+-	// this is a routed packet
+-	if ((*pskb)->physindev == NULL)
+-		return NF_ACCEPT;
+-	if ((*pskb)->physindev != &__fake_net_device)
+-		printk("ebtables (br_nat_src): physindev hack "
+-		       "doesn't work - BUG\n");
+-
+-	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+-}
+-
+-static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *))
+-{
+-	// this is a bridged packet
+-	if ((*pskb)->physindev == &__fake_net_device)
+-		return NF_ACCEPT;
+-	if ((*pskb)->physindev)
+-		printk("ebtables (br_nat_src_route): physindev hack "
+-		       "doesn't work - BUG\n");
+-
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+ 
+@@ -111,15 +62,9 @@
+ 	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
+ 	   NF_BR_PRI_NAT_DST_OTHER},
+ 	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
+-	   NF_BR_PRI_NAT_SRC_BRIDGED},
+-	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
+-	   NF_BR_PRI_NAT_SRC_OTHER},
++	   NF_BR_PRI_NAT_SRC},
+ 	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
+ 	   NF_BR_PRI_NAT_DST_BRIDGED},
+-	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
+-	   NF_BR_PRI_FILTER_OTHER + 1},
+-	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
+-	   NF_BR_PRI_FILTER_OTHER + 1}
+ };
+ 
+ static int __init init(void)
+--- linux/net/bridge/netfilter/ebt_redirect.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_redirect.c	Wed Jul 31 19:04:30 2002
+@@ -38,6 +38,10 @@
+ {
+ 	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+ 
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ 	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark.c	Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,73 @@
++/*
++ *  ebt_mark_t
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++// The target member of the struct ebt_vlan_info provides the same
++// functionality as a separate table
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++	if ((*pskb)->nfmark != infostuff->mark) {
++		(*pskb)->nfmark = infostuff->mark;
++		(*pskb)->nfcache |= NFC_ALTERED;
++	}
++	return infostuff->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	if (datalen != sizeof(struct ebt_mark_t_info))
++		return -EINVAL;
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target mark_target =
++{
++	{NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++	ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark_m.c	Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,62 @@
++/*
++ *  ebt_mark_m
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++   const struct net_device *in, const struct net_device *out, const void *data,
++   unsigned int datalen, const struct ebt_counter *c)
++{
++	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & EBT_MARK_OR)
++		return !(!!(skb->nfmark & info->mask) ^ info->invert);
++	return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++        struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++	if (info->bitmask & ~EBT_MARK_MASK)
++		return -EINVAL;
++	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++		return -EINVAL;
++	if (!info->bitmask)
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_mark_m_info)) {
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++	{NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++	THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- linux/net/bridge/netfilter/ebt_snat.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_snat.c	Wed Jul 31 19:04:30 2002
+@@ -31,6 +31,10 @@
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if (strcmp(tablename, "nat"))
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+--- linux/net/bridge/netfilter/ebt_dnat.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_dnat.c	Wed Jul 31 19:04:30 2002
+@@ -31,6 +31,10 @@
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
++	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++	   infostuff->target == EBT_RETURN)
++		return -EINVAL;
++	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if ( (strcmp(tablename, "nat") ||
+ 	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ 	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+--- linux/net/bridge/netfilter/ebtables.c	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebtables.c	Wed Jul 31 19:04:30 2002
+@@ -4,7 +4,7 @@
+  *  Author:
+  *  Bart De Schuymer		<bart.de.schuymer@pandora.be>
+  *
+- *  ebtables.c,v 2.0, April, 2002
++ *  ebtables.c,v 2.0, July, 2002
+  *
+  *  This code is stongly inspired on the iptables code which is
+  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+@@ -50,6 +50,21 @@
+                                          ": out of memory: "format, ## args)
+ // #define MEMPRINT(format, args...)
+ 
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++   COUNTER_OFFSET(n) * cpu))
++
++
++
+ static void print_string(char *str);
+ 
+ static DECLARE_MUTEX(ebt_mutex);
+@@ -62,10 +77,8 @@
+ { {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+ 
+ static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+-	    const struct sk_buff *skb,
+-	    const struct net_device *in,
+-	    const struct net_device *out,
+-	    const struct ebt_counter *c)
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const struct ebt_counter *c)
+ {
+ 	w->u.watcher->watcher(skb, in, out, w->data,
+ 	   w->watcher_size, c);
+@@ -74,10 +87,8 @@
+ }
+ 
+ static inline int ebt_do_match (struct ebt_entry_match *m,
+-	    const struct sk_buff *skb,
+-	    const struct net_device *in,
+-	    const struct net_device *out,
+-	    const struct ebt_counter *c)
++   const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const struct ebt_counter *c)
+ {
+ 	return m->u.match->match(skb, in, out, m->data,
+ 	   m->match_size, c);
+@@ -89,7 +100,51 @@
+ 		return 0;
+ 	if (!device)
+ 		return 1;
+-	return strncmp(entry, device->name, IFNAMSIZ);
++	return !!strncmp(entry, device->name, IFNAMSIZ);
++}
++
++#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++   const struct net_device *in, const struct net_device *out)
++{
++	int verdict, i;
++
++	if (p->bitmask & EBT_802_3) {
++		if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++			return 1;
++	} else if (!(p->bitmask & EBT_NOPROTO) &&
++	   FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++		return 1;
++
++	if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++		return 1;
++	if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++		return 1;
++	if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
++	   p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++		return 1;
++	if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
++	   (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++		return 1;
++	
++	if (p->bitmask & EBT_SOURCEMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
++			   p->sourcemsk[i];
++		if (FWINV(verdict != 0, EBT_ISOURCE) )
++			return 1;
++	}
++	if (p->bitmask & EBT_DESTMAC) {
++		verdict = 0;
++		for (i = 0; i < 6; i++)
++			verdict |= (h->h_dest[i] ^ p->destmac[i]) &
++			   p->destmsk[i];
++		if (FWINV(verdict != 0, EBT_IDEST) )
++			return 1;
++	}
++	return 0;
+ }
+ 
+ // Do some firewalling
+@@ -97,9 +152,9 @@
+    const struct net_device *in, const struct net_device *out,
+    struct ebt_table *table)
+ {
+-	int i, j, nentries;
++	int i, nentries;
+ 	struct ebt_entry *point;
+-	struct ebt_counter *counter_base;
++	struct ebt_counter *counter_base, *cb_base;
+ 	struct ebt_entry_target *t;
+ 	int verdict, sp = 0;
+ 	struct ebt_chainstack *cs;
+@@ -108,6 +163,8 @@
+ 	struct ebt_table_info *private = table->private;
+ 
+ 	read_lock_bh(&table->lock);
++	cb_base = COUNTER_BASE(private->counters, private->nentries, \
++	   cpu_number_map(smp_processor_id()));
+ 	if (private->chainstack)
+ 		cs = private->chainstack[cpu_number_map(smp_processor_id())];
+ 	else
+@@ -115,117 +172,83 @@
+ 	chaininfo = private->hook_entry[hook];
+ 	nentries = private->hook_entry[hook]->nentries;
+ 	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+-	#define cb_base table->private->counters + \
+-	   cpu_number_map(smp_processor_id()) * table->private->nentries
+ 	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+-	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
+ 	// base for chain jumps
+ 	base = (char *)chaininfo;
+ 	i = 0;
+- 	while (i < nentries) {
+-		if ( ( point->bitmask & EBT_NOPROTO ||
+-		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
+-		      EBT_IPROTO)
+-		   || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
+-		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
+-		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
+-		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
+-		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
+-		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
+-		   && ((!out || !out->br_port) ? 1 :
+-		       FWINV(!ebt_dev_check((char *)
+-		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+-
+-		) {
+-			if (point->bitmask & EBT_SOURCEMAC) {
+-				verdict = 0;
+-				for (j = 0; j < 6; j++)
+-					verdict |= (((**pskb).mac.ethernet)->
+-					   h_source[j] ^ point->sourcemac[j]) &
+-					   point->sourcemsk[j];
+-				if (FWINV(!!verdict, EBT_ISOURCE) )
+-					goto letscontinue;
+-			}
++	while (i < nentries) {
++		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++			goto letscontinue;
+ 
+-			if (point->bitmask & EBT_DESTMAC) {
+-				verdict = 0;
+-				for (j = 0; j < 6; j++)
+-					verdict |= (((**pskb).mac.ethernet)->
+-					   h_dest[j] ^ point->destmac[j]) &
+-					   point->destmsk[j];
+-				if (FWINV(!!verdict, EBT_IDEST) )
+-					goto letscontinue;
+-			}
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++		   out, counter_base + i) != 0)
++			goto letscontinue;
+ 
+-			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
+-			   out, counter_base + i) != 0)
+-				goto letscontinue;
++		// increase counter
++		(*(counter_base + i)).pcnt++;
+ 
+-			// increase counter
+-			(*(counter_base + i)).pcnt++;
++		// these should only watch: not modify, nor tell us
++		// what to do with the packet
++		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++		   out, counter_base + i);
+ 
+-			// these should only watch: not modify, nor tell us
+-			// what to do with the packet
+-			EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+-			   out, counter_base + i);
+-
+-			t = (struct ebt_entry_target *)
+-			   (((char *)point) + point->target_offset);
+-			// standard target
+-			if (!t->u.target->target)
+-				verdict =
+-				   ((struct ebt_standard_target *)t)->verdict;
+-			else
+-				verdict = t->u.target->target(pskb, hook,
+-				   in, out, t->data, t->target_size);
+-			if (verdict == EBT_ACCEPT) {
+-				read_unlock_bh(&table->lock);
+-				return NF_ACCEPT;
+-			}
+-			if (verdict == EBT_DROP) {
+-				read_unlock_bh(&table->lock);
+-				return NF_DROP;
+-			}
+-			if (verdict == EBT_RETURN) {
++		t = (struct ebt_entry_target *)
++		   (((char *)point) + point->target_offset);
++		// standard target
++		if (!t->u.target->target)
++			verdict = ((struct ebt_standard_target *)t)->verdict;
++		else
++			verdict = t->u.target->target(pskb, hook,
++			   in, out, t->data, t->target_size);
++		if (verdict == EBT_ACCEPT) {
++			read_unlock_bh(&table->lock);
++			return NF_ACCEPT;
++		}
++		if (verdict == EBT_DROP) {
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		if (verdict == EBT_RETURN) {
+ letsreturn:
+-				if (sp == 0)
+-					// act like this is EBT_CONTINUE
+-					goto letscontinue;
+-				sp--;
+-				// put all the local variables right
+-				i = cs[sp].n;
+-				chaininfo = cs[sp].chaininfo;
+-				nentries = chaininfo->nentries;
+-				point = cs[sp].e;
+-				counter_base = cb_base +
+-				   chaininfo->counter_offset;
+-				continue;
+-			}
+-			if (verdict == EBT_CONTINUE)
++			if (sp == 0) {
++				BUGPRINT("RETURN on base chain");
++				// act like this is EBT_CONTINUE
+ 				goto letscontinue;
+-			if (verdict < 0) {
+-				BUGPRINT("bogus standard verdict\n");
+-				read_unlock_bh(&table->lock);
+-				return NF_DROP;
+-			}
+-			// jump to a udc
+-			cs[sp].n = i + 1;
+-			cs[sp].chaininfo = chaininfo;
+-			cs[sp].e = (struct ebt_entry *)
+-			   (((char *)point) + point->next_offset);
+-			i = 0;
+-			chaininfo = (struct ebt_entries *) (base + verdict);
+-			if (chaininfo->distinguisher) {
+-				BUGPRINT("jump to non-chain\n");
+-				read_unlock_bh(&table->lock);
+-				return NF_DROP;
+ 			}
++			sp--;
++			// put all the local variables right
++			i = cs[sp].n;
++			chaininfo = cs[sp].chaininfo;
+ 			nentries = chaininfo->nentries;
+-			point = (struct ebt_entry *)chaininfo->data;
+-			counter_base = cb_base + chaininfo->counter_offset;
+-			sp++;
++			point = cs[sp].e;
++			counter_base = cb_base +
++			   chaininfo->counter_offset;
+ 			continue;
+ 		}
++		if (verdict == EBT_CONTINUE)
++			goto letscontinue;
++		if (verdict < 0) {
++			BUGPRINT("bogus standard verdict\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		// jump to a udc
++		cs[sp].n = i + 1;
++		cs[sp].chaininfo = chaininfo;
++		cs[sp].e = (struct ebt_entry *)
++		   (((char *)point) + point->next_offset);
++		i = 0;
++		chaininfo = (struct ebt_entries *) (base + verdict);
++		if (chaininfo->distinguisher) {
++			BUGPRINT("jump to non-chain\n");
++			read_unlock_bh(&table->lock);
++			return NF_DROP;
++		}
++		nentries = chaininfo->nentries;
++		point = (struct ebt_entry *)chaininfo->data;
++		counter_base = cb_base + chaininfo->counter_offset;
++		sp++;
++		continue;
+ letscontinue:
+ 		point = (struct ebt_entry *)
+ 		   (((char *)point) + point->next_offset);
+@@ -284,7 +307,6 @@
+ 		request_module(modulename);
+ 		ret = find_inlist_lock_noload(head, name, error, mutex);
+ 	}
+-
+ 	return ret;
+ }
+ #endif
+@@ -320,9 +342,12 @@
+ 	struct ebt_match *match;
+ 	int ret;
+ 
++	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++	   ((char *)e) + e->watchers_offset)
++		return -EINVAL;
+ 	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+-	if (!match) 
++	if (!match)
+ 		return ret;
+ 	m->u.match = match;
+ 	if (match->me)
+@@ -346,9 +371,12 @@
+ 	struct ebt_watcher *watcher;
+ 	int ret;
+ 
++	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++	   ((char *)e) + e->target_offset)
++		return -EINVAL;
+ 	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+-	if (!watcher) 
++	if (!watcher)
+ 		return ret;
+ 	w->u.watcher = watcher;
+ 	if (watcher->me)
+@@ -547,14 +575,16 @@
+ 		else
+ 			break;
+ 	}
++	// (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++	// a base chain
+ 	if (i < NF_BR_NUMHOOKS)
+-		hookmask = (1 << hook);
++		hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ 	else {
+ 		for (i = 0; i < udc_cnt; i++)
+ 			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+ 				break;
+ 		if (i == 0)
+-			hookmask = (1 << hook);
++			hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ 		else
+ 			hookmask = cl_s[i - 1].hookmask;
+ 	}
+@@ -589,9 +619,10 @@
+ 			ret = -EFAULT;
+ 			goto cleanup_watchers;
+ 		}
+-	} else if (t->u.target->check &&
+-	   t->u.target->check(name, hookmask, e, t->data,
+-	   t->target_size) != 0) {
++	} else if ((e->target_offset + t->target_size +
++	   sizeof(struct ebt_entry_target) > e->next_offset) ||
++	   (t->u.target->check &&
++	   t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+ 		if (t->u.target->me)
+ 			__MOD_DEC_USE_COUNT(t->u.target->me);
+ 		ret = -EFAULT;
+@@ -611,7 +642,7 @@
+ {
+ 	struct ebt_entry_target *t;
+ 
+-	if (e->bitmask == 0)
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ 		return 0;
+ 	// we're done
+ 	if (cnt && (*cnt)-- == 0)
+@@ -643,7 +674,8 @@
+ 			// put back values of the time when this chain was called
+ 			e = cl_s[chain_nr].cs.e;
+ 			if (cl_s[chain_nr].from != -1)
+-				nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++				nentries =
++				cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+ 			else
+ 				nentries = chain->nentries;
+ 			pos = cl_s[chain_nr].cs.n;
+@@ -679,6 +711,7 @@
+ 				BUGPRINT("loop\n");
+ 				return -1;
+ 			}
++			// this can't be 0, so the above test is correct
+ 			cl_s[i].cs.n = pos + 1;
+ 			pos = 0;
+ 			cl_s[i].cs.e = ((void *)e + e->next_offset);
+@@ -784,7 +817,7 @@
+ 					vfree(newinfo->chainstack[--i]);
+ 				vfree(newinfo->chainstack);
+ 				newinfo->chainstack = NULL;
+-				break;
++				return -ENOMEM;
+ 			}
+ 		}
+ 
+@@ -824,7 +857,6 @@
+ 	//   beginning of a chain. This can only occur in chains that
+ 	//   are not accessible from any base chains, so we don't care.
+ 
+-	// we just don't trust anything
+ 	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	// used to know what we need to clean up if something goes wrong
+ 	i = 0;
+@@ -841,27 +873,27 @@
+ }
+ 
+ // called under write_lock
+-static inline void get_counters(struct ebt_counter *oldcounters,
++static void get_counters(struct ebt_counter *oldcounters,
+    struct ebt_counter *counters, unsigned int nentries)
+ {
+-	int i, cpu, counter_base;
++	int i, cpu;
++	struct ebt_counter *counter_base;
+ 
+ 	// counters of cpu 0
+ 	memcpy(counters, oldcounters,
+ 	   sizeof(struct ebt_counter) * nentries);
+ 	// add other counters to those of cpu 0
+ 	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
+-		counter_base = cpu * nentries;
++		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+ 		for (i = 0; i < nentries; i++)
+-			counters[i].pcnt +=
+-			   oldcounters[counter_base + i].pcnt;
++			counters[i].pcnt += counter_base[i].pcnt;
+ 	}
+ }
+ 
+ // replace the table
+ static int do_replace(void *user, unsigned int len)
+ {
+-	int ret, i;
++	int ret, i, countersize;
+ 	struct ebt_table_info *newinfo;
+ 	struct ebt_replace tmp;
+ 	struct ebt_table *t;
+@@ -869,7 +901,7 @@
+ 	// used to be able to unlock earlier
+ 	struct ebt_table_info *table;
+ 
+- 	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++	if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ 		return -EFAULT;
+ 
+ 	if (len != sizeof(tmp) + tmp.entries_size) {
+@@ -881,28 +913,19 @@
+ 		BUGPRINT("Entries_size never zero\n");
+ 		return -EINVAL;
+ 	}
++	countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
+ 	newinfo = (struct ebt_table_info *)
+-	   vmalloc(sizeof(struct ebt_table_info));
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+ 	if (!newinfo)
+ 		return -ENOMEM;
+ 
+-	if (tmp.nentries) {
+-		newinfo->counters = (struct ebt_counter *)vmalloc(
+-		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
+-		if (!newinfo->counters) {
+-			ret = -ENOMEM;
+-			goto free_newinfo;
+-		}
+-		memset(newinfo->counters, 0,
+-		   sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
+-	}
+-	else
+-		newinfo->counters = NULL;
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
+ 
+ 	newinfo->entries = (char *)vmalloc(tmp.entries_size);
+ 	if (!newinfo->entries) {
+ 		ret = -ENOMEM;
+-		goto free_counters;
++		goto free_newinfo;
+ 	}
+ 	if (copy_from_user(
+ 	   newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+@@ -933,7 +956,7 @@
+ 
+ 	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ 	if (!t)
+-		goto free_unlock;
++		goto free_iterate;
+ 
+ 	// the table doesn't like it
+ 	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+@@ -974,8 +997,6 @@
+ 	   ebt_cleanup_entry, NULL);
+ 
+ 	vfree(table->entries);
+-	if (table->counters)
+-		vfree(table->counters);
+ 	if (table->chainstack) {
+ 		for (i = 0; i < smp_num_cpus; i++)
+ 			vfree(table->chainstack[i]);
+@@ -989,6 +1010,7 @@
+ 
+ free_unlock:
+ 	up(&ebt_mutex);
++free_iterate:
+ 	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ 	   ebt_cleanup_entry, NULL);
+ free_counterstmp:
+@@ -1003,9 +1025,6 @@
+ free_entries:
+ 	if (newinfo->entries)
+ 		vfree(newinfo->entries);
+-free_counters:
+-	if (newinfo->counters)
+-		vfree(newinfo->counters);
+ free_newinfo:
+ 	if (newinfo)
+ 		vfree(newinfo);
+@@ -1090,7 +1109,7 @@
+ int ebt_register_table(struct ebt_table *table)
+ {
+ 	struct ebt_table_info *newinfo;
+-	int ret, i;
++	int ret, i, countersize;
+ 
+ 	if (!table || !table->table ||!table->table->entries ||
+ 	    table->table->entries_size == 0 ||
+@@ -1099,8 +1118,9 @@
+ 		return -EINVAL;
+ 	}
+ 
++	countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
+ 	newinfo = (struct ebt_table_info *)
+-	   vmalloc(sizeof(struct ebt_table_info));
++	   vmalloc(sizeof(struct ebt_table_info) + countersize);
+ 	ret = -ENOMEM;
+ 	if (!newinfo)
+ 		return -ENOMEM;
+@@ -1112,24 +1132,15 @@
+ 	memcpy(newinfo->entries, table->table->entries,
+ 	   table->table->entries_size);
+ 
+-	if (table->table->nentries) {
+-		newinfo->counters = (struct ebt_counter *)
+-		   vmalloc(table->table->nentries *
+-		   sizeof(struct ebt_counter) * smp_num_cpus);
+-		if (!newinfo->counters)
+-			goto free_entries;
+-		memset(newinfo->counters, 0, table->table->nentries *
+-		   sizeof(struct ebt_counter) * smp_num_cpus);
+-	}
+-	else
+-		newinfo->counters = NULL;
++	if (countersize)
++		memset(newinfo->counters, 0, countersize);
+ 
+ 	// fill in newinfo and parse the entries
+ 	newinfo->chainstack = NULL;
+ 	ret = translate_table(table->table, newinfo);
+ 	if (ret != 0) {
+ 		BUGPRINT("Translate_table failed\n");
+-		goto free_counters;
++		goto free_chainstack;
+ 	}
+ 
+ 	if (table->check && table->check(newinfo, table->valid_hooks)) {
+@@ -1141,7 +1152,7 @@
+ 	table->lock = RW_LOCK_UNLOCKED;
+ 	ret = down_interruptible(&ebt_mutex);
+ 	if (ret != 0)
+-		goto free_counters;
++		goto free_chainstack;
+ 
+ 	if (list_named_find(&ebt_tables, table->name)) {
+ 		ret = -EEXIST;
+@@ -1155,15 +1166,12 @@
+ 	return 0;
+ free_unlock:
+ 	up(&ebt_mutex);
+-free_counters:
+-	if (newinfo->counters)
+-		vfree(newinfo->counters);
++free_chainstack:
+ 	if (newinfo->chainstack) {
+ 		for (i = 0; i < smp_num_cpus; i++)
+ 			vfree(newinfo->chainstack[i]);
+ 		vfree(newinfo->chainstack);
+ 	}
+-free_entries:
+ 	vfree(newinfo->entries);
+ free_newinfo:
+ 	vfree(newinfo);
+@@ -1183,8 +1191,6 @@
+ 	up(&ebt_mutex);
+ 	EBT_ENTRY_ITERATE(table->private->entries,
+ 	   table->private->entries_size, ebt_cleanup_entry, NULL);
+-	if (table->private->counters)
+-		vfree(table->private->counters);
+ 	if (table->private->entries)
+ 		vfree(table->private->entries);
+ 	if (table->private->chainstack) {
+@@ -1219,7 +1225,6 @@
+ 	}
+ 
+ 	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+-
+ 	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ 	if (!t)
+ 		goto free_tmp;
+@@ -1344,7 +1349,7 @@
+ 		counterstmp = (struct ebt_counter *)
+ 		   vmalloc(nentries * sizeof(struct ebt_counter));
+ 		if (!counterstmp) {
+-			BUGPRINT("Couldn't copy counters, out of memory\n");
++			MEMPRINT("Couldn't copy counters, out of memory\n");
+ 			return -ENOMEM;
+ 		}
+ 		write_lock_bh(&t->lock);
+--- linux/include/linux/netfilter_bridge/ebtables.h	Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebtables.h	Wed Jul 31 19:04:30 2002
+@@ -12,9 +12,9 @@
+ 
+ #ifndef __LINUX_BRIDGE_EFF_H
+ #define __LINUX_BRIDGE_EFF_H
+-#include <linux/if.h> // IFNAMSIZ
++#include <linux/if.h>
+ #include <linux/netfilter_bridge.h>
+-#include <linux/if_ether.h> // ETH_ALEN
++#include <linux/if_ether.h>
+ 
+ #define EBT_TABLE_MAXNAMELEN 32
+ #define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+@@ -46,10 +46,10 @@
+ };
+ 
+ struct ebt_entries {
+-	// this field is always set to zero (including userspace).
++	// this field is always set to zero
+ 	// See EBT_ENTRY_OR_ENTRIES.
+ 	// Must be same size as ebt_entry.bitmask
+-	__u32 distinguisher;
++	unsigned int distinguisher;
+ 	// the chain name
+ 	char name[EBT_CHAIN_MAXNAMELEN];
+ 	// counter offset for this chain
+@@ -133,27 +133,27 @@
+ // one entry
+ struct ebt_entry {
+ 	// this needs to be the first field
+-	__u32 bitmask;
+-	__u32 invflags;
++	unsigned int bitmask;
++	unsigned int invflags;
+ 	__u16 ethproto;
+ 	// the physical in-dev
+-	__u8 in[IFNAMSIZ];
++	char in[IFNAMSIZ];
+ 	// the logical in-dev
+-	__u8 logical_in[IFNAMSIZ];
++	char logical_in[IFNAMSIZ];
+ 	// the physical out-dev
+-	__u8 out[IFNAMSIZ];
++	char out[IFNAMSIZ];
+ 	// the logical out-dev
+-	__u8 logical_out[IFNAMSIZ];
+-	__u8 sourcemac[ETH_ALEN];
+-	__u8 sourcemsk[ETH_ALEN];
+-	__u8 destmac[ETH_ALEN];
+-	__u8 destmsk[ETH_ALEN];
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
+ 	// sizeof ebt_entry + matches
+-	__u16 watchers_offset;
++	unsigned int watchers_offset;
+ 	// sizeof ebt_entry + matches + watchers
+-	__u16 target_offset;
++	unsigned int target_offset;
+ 	// sizeof ebt_entry + matches + watchers + target
+-	__u16 next_offset;
++	unsigned int next_offset;
+ 	unsigned char elems[0];
+ };
+ 
+@@ -238,10 +238,10 @@
+ 	unsigned int nentries;
+ 	// pointers to the start of the chains
+ 	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+-	struct ebt_counter *counters;
+ 	// room to maintain the stack used for jumping from and into udc
+ 	struct ebt_chainstack **chainstack;
+ 	char *entries;
++	struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
+ };
+ 
+ struct ebt_table
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_t.h	Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++	unsigned long mark;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_m.h	Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++	unsigned long mark, mask;
++	__u8 invert;
++	__u8 bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre10.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre10.001.diff
new file mode 100644
index 0000000..d84488e
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre10.001.diff
@@ -0,0 +1,418 @@
+--- ebt2.0pre9/net/bridge/netfilter/ebt_vlan.c	Thu Jun 27 19:11:50 2002
++++ ebt2.0pre10.001/net/bridge/netfilter/ebt_vlan.c	Sat Jul  6 10:52:44 2002
+@@ -1,120 +1,284 @@
+ /*
+- *  ebt_vlan kernelspace
+- *
+- *      Authors:
+- *      Bart De Schuymer <bart.de.schuymer@pandora.be>
+- *      Nick Fedchik <nick@fedchik.org.ua>
+- *
+- *      June, 2002
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ *          Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *    
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ * 
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *  
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
+ 
+-#include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge/ebt_vlan.h>
+-#include <linux/if_vlan.h>
+ #include <linux/if_ether.h>
++#include <linux/if_vlan.h>
+ #include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
+ 
+ static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
+ MODULE_PARM (debug, "0-1b");
+ MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++		    MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
+ 
+-#define MODULE_VERSION "0.2"
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
+ 
+-static int ebt_filter_vlan (const struct sk_buff *skb,
+-			    const struct net_device *in,
+-			    const struct net_device *out,
+-			    const void *data,
+-			    unsigned int datalen,
+-			    const struct ebt_counter *c)
++/*
++ * Function description: ebt_filter_vlan() is main engine for 
++ * checking passed 802.1Q frame according to 
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in  -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++		 const struct net_device *in,
++		 const struct net_device *out,
++		 const void *data,
++		 unsigned int datalen, const struct ebt_counter *c)
+ {
+-	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
+-	struct vlan_ethhdr *vlanethhdr =
+-	    (struct vlan_ethhdr *) skb->mac.raw;
+-	unsigned short v_id;
+-	unsigned short v_prio;
+-	unsigned short v_TCI;
+-
+-	/*
+-	 * Calculate 802.1Q VLAN ID and user_priority from 
+-	 * Tag Control Information (TCI) field.
+-	 * Reserved one bit (13) for CFI (Canonical Format Indicator)
+-	 */
+-	v_TCI = ntohs (vlanethhdr->h_vlan_TCI);
+-	v_id = v_TCI & 0xFFF;
+-	v_prio = v_TCI >> 13;
+-
+-	/*
+-	 * Checking VLANs 
+-	 */
+-	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
+-		if (!((infostuff->id == v_id)
+-		      ^ !!(infostuff->invflags & EBT_VLAN_ID)))
+-			return 1;
+-		if (debug)
+-			printk (KERN_DEBUG
+-				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
+-				(infostuff->
+-				 invflags & EBT_VLAN_ID) ? "!" : "",
+-				infostuff->id, infostuff->bitmask);
+-	}
+-	/*
+-	 * Checking User Priority 
+-	 */
+-	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
+-		if (!((infostuff->prio == v_prio)
+-		      ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
+-			return 1;	/* missed */
+-		if (debug)
+-			printk (KERN_DEBUG
+-				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
+-				(infostuff->
+-				 invflags & EBT_VLAN_PRIO) ? "!" : "",
+-				infostuff->prio, infostuff->bitmask);
+-	}
+-	/*
+-	 * Checking for Encapsulated proto
+-	 */
+-	if (infostuff->bitmask & EBT_VLAN_ENCAP) {	/* Is VLAN Encap parsed? */
+-		if (!
+-		    ((infostuff->encap ==
+-		      vlanethhdr->h_vlan_encapsulated_proto)
+-		     ^ !!(infostuff->invflags & EBT_VLAN_ENCAP)))
+-			return 1;	/* missed */
+-		if (debug)
+-			printk (KERN_DEBUG
+-				"ebt_vlan: matched encap=%s%2.4X (mask=%X)\n",
+-				(infostuff->
+-				 invflags & EBT_VLAN_ENCAP) ? "!" : "",
+-				ntohs (infostuff->encap),
+-				infostuff->bitmask);
+-	}
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
++
++	unsigned short TCI;	/* Whole TCI, given from parsed frame */
++	unsigned short id;	/* VLAN ID, given from frame TCI */
++	unsigned char prio;	/* user_priority, given from frame TCI */
++	unsigned short encap;	/* VLAN encapsulated Type/Length field, given from orig frame */
+ 
+ 	/*
+-	 * rule matched 
++	 * Tag Control Information (TCI) consists of the following elements:
++	 * - User_priority. This field allows the tagged frame to carry user_priority
++	 * information across Bridged LANs in which individual LAN segments may be unable to signal
++	 * priority information (e.g., 802.3/Ethernet segments). 
++	 * The user_priority field is three bits in length, 
++	 * interpreted as a binary number. The user_priority is therefore
++	 * capable of representing eight priority levels, 0 through 7. 
++	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++	 * - Canonical Format Indicator (CFI). This field is used,
++	 * in 802.3/Ethernet, to signal the presence or absence
++	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++	 * in the RIF, to signal the bit order of address information carried in the encapsulated
++	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
++	 * uniquely identify the VLAN to which the frame belongs. 
++	 * The VID is encoded as an unsigned binary number. 
++	 */
++	TCI = ntohs (frame->h_vlan_TCI);
++	id = TCI & 0xFFF;
++	prio = TCI >> 13;
++	encap = frame->h_vlan_encapsulated_proto;
++
++	/*
++	 * First step is to check is null VLAN ID present
++	 * in the parsed frame
++	 */
++	if (!(id)) {
++		/*
++		 * Checking VLAN Identifier (VID)
++		 */
++		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++			DEBUG_MSG
++			    ("matched rule id=%s%d for frame id=%d\n",
++			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++		}
++	} else {
++		/*
++		 * Checking user_priority
++		 */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++			DEBUG_MSG
++			    ("matched rule prio=%s%d for frame prio=%d\n",
++			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     prio);
++		}
++	}
++	/*
++	 * Checking Encapsulated Proto (Length/Type) field
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++			   INV_FLAG (EBT_VLAN_ENCAP),
++			   ntohs (infostuff->encap), ntohs (encap));
++	}
++	/*
++	 * All possible extension parameters was parsed.
++	 * If rule never returned by missmatch, then all ok.
+ 	 */
+ 	return 0;
+ }
+ 
+ /*
+- * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
+- * * it is called to check that userspace doesn't give a bad table.
++ * Function description: ebt_vlan_check() is called when userspace 
++ * delivers the table to the kernel, 
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
+  */
+-static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
+-			   const struct ebt_entry *e, void *data,
+-			   unsigned int datalen)
++static int
++ebt_check_vlan (const char *tablename,
++		unsigned int hooknr,
++		const struct ebt_entry *e, void *data,
++		unsigned int datalen)
+ {
+ 	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
+ 
+-	if (datalen != sizeof (struct ebt_vlan_info))
++	/*
++	 * Parameters buffer overflow check 
++	 */
++	if (datalen != sizeof (struct ebt_vlan_info)) {
++		DEBUG_MSG
++		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof (struct ebt_vlan_info));
+ 		return -EINVAL;
++	}
+ 
+-	if (e->ethproto != __constant_htons (ETH_P_8021Q))
++	/*
++	 * Is it 802.1Q frame checked?
++	 */
++	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++			   (unsigned short) ntohs (e->ethproto));
+ 		return -EINVAL;
++	}
+ 
++	/*
++	 * Check for bitmask range 
++	 * True if even one bit is out of mask
++	 */
+ 	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++			   infostuff->bitmask, EBT_VLAN_MASK);
++		return -EINVAL;
++	}
++
++	/*
++	 * Check for inversion flags range 
++	 */
++	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++			   infostuff->invflags, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
++	/*
++	 * Reserved VLAN ID (VID) values
++	 * -----------------------------
++	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++	 * no VLAN identifier is present in the frame. This VID value shall not be
++	 * configured as a PVID, configured in any Filtering Database entry, or used in any
++	 * Management operation.
++	 * 
++	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++	 * Port. The PVID value can be changed by management on a per-Port basis.
++	 * 
++	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++	 * PVID or transmitted in a tag header.
++	 * 
++	 * The remaining values of VID are available for general use as VLAN identifiers.
++	 * A Bridge may implement the ability to support less than the full range of VID values; 
++	 * i.e., for a given implementation,
++	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++	 * VID, N.
++	 * 
++	 * For Linux, N = 4094.
++	 */
++	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++		if (!!infostuff->id) {	/* if id!=0 => check vid range */
++			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++				DEBUG_MSG
++				    ("vlan id %d is out of range (1-4094)\n",
++				     infostuff->id);
++				return -EINVAL;
++			}
++			/*
++			 * Note: This is valid VLAN-tagged frame point.
++			 * Any value of user_priority are acceptable, but could be ignored
++			 * according to 802.1Q Std.
++			 */
++		} else {
++			/*
++			 * if id=0 (null VLAN ID)  => Check for user_priority range 
++			 */
++			if (GET_BITMASK (EBT_VLAN_PRIO)) {
++				if ((unsigned char) infostuff->prio > 7) {
++					DEBUG_MSG
++					    ("prio %d is out of range (0-7)\n",
++					     infostuff->prio);
++					return -EINVAL;
++				}
++			}
++			/*
++			 * Note2: This is valid priority-tagged frame point
++			 * with null VID field.
++			 */
++		}
++	} else {		/* VLAN Id not set */
++		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
++			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		}
++	}
++	/*
++	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
++	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
++	 * a Bridge may adjust the padding field such that
++	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++			DEBUG_MSG
++			    ("encap packet length %d is less than minimal %d\n",
++			     ntohs (infostuff->encap), ETH_ZLEN);
++			return -EINVAL;
++		}
++	}
++
++	/*
++	 * Otherwise is all correct 
++	 */
++	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++		   tablename, hooknr);
+ 	return 0;
+ }
+ 
+@@ -122,22 +286,27 @@
+ 	{NULL, NULL},
+ 	EBT_VLAN_MATCH,
+ 	ebt_filter_vlan,
+-	ebt_vlan_check,
++	ebt_check_vlan,
+ 	NULL,
+ 	THIS_MODULE
+ };
+ 
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
+ static int __init init (void)
+ {
+-	printk (KERN_INFO
+-		"ebt_vlan: 802.1Q VLAN matching module for EBTables "
+-		MODULE_VERSION "\n");
+-	if (debug)
+-		printk (KERN_DEBUG
+-			"ebt_vlan: 802.1Q rule matching debug is on\n");
++	DEBUG_MSG ("ebtables 802.1Q extension module v"
++		   MODULE_VERSION "\n");
++	DEBUG_MSG ("module debug=%d\n", !!debug);
+ 	return ebt_register_match (&filter_vlan);
+ }
+ 
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
+ static void __exit fini (void)
+ {
+ 	ebt_unregister_match (&filter_vlan);
+@@ -145,8 +314,5 @@
+ 
+ module_init (init);
+ module_exit (fini);
++
+ EXPORT_NO_SYMBOLS;
+-MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
+-MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v"
+-		    MODULE_VERSION);
+-MODULE_LICENSE ("GPL");
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre11.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre11.001.diff
new file mode 100644
index 0000000..13f0100
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre11.001.diff
@@ -0,0 +1,388 @@
+--- linux/net/bridge/netfilter/ebtable_broute.c	Tue Jul 16 18:49:14 2002
++++ ebt2.0pre11.001/net/bridge/netfilter/ebtable_broute.c	Tue Jul  9 20:04:31 2002
+@@ -21,7 +21,7 @@
+ // EBT_ACCEPT means the frame will be bridged
+ // EBT_DROP means the frame will be routed
+ static struct ebt_entries initial_chain =
+-  {0, "BROUTE", 0, EBT_ACCEPT, 0};
++  {0, "BROUTING", 0, EBT_ACCEPT, 0};
+ 
+ static struct ebt_replace initial_table =
+ {
+--- linux/net/bridge/netfilter/ebt_redirect.c	Tue Jul 16 18:49:14 2002
++++ ebt2.0pre11.001/net/bridge/netfilter/ebt_redirect.c	Tue Jul  9 15:41:46 2002
+@@ -22,9 +22,14 @@
+ {
+ 	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+ 
+-	memcpy((**pskb).mac.ethernet->h_dest,
+-	   in->br_port->br->dev.dev_addr, ETH_ALEN);
+-	(*pskb)->pkt_type = PACKET_HOST;
++	if (hooknr != NF_BR_BROUTING)
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->br_port->br->dev.dev_addr, ETH_ALEN);
++	else {
++		memcpy((**pskb).mac.ethernet->h_dest,
++		   in->dev_addr, ETH_ALEN);
++		(*pskb)->pkt_type = PACKET_HOST;
++	}
+ 	return infostuff->target;
+ }
+ 
+--- linux/net/bridge/netfilter/ebtables.c	Tue Jul 16 18:49:15 2002
++++ ebt2.0pre11.001/net/bridge/netfilter/ebtables.c	Sun Jul 14 20:13:16 2002
+@@ -105,15 +105,19 @@
+ 	struct ebt_chainstack *cs;
+ 	struct ebt_entries *chaininfo;
+ 	char *base;
++	struct ebt_table_info *private = table->private;
+ 
+ 	read_lock_bh(&table->lock);
+-	cs = table->private->chainstack;
+-	chaininfo = table->private->hook_entry[hook];
+-	nentries = table->private->hook_entry[hook]->nentries;
+-	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++	if (private->chainstack)
++		cs = private->chainstack[cpu_number_map(smp_processor_id())];
++	else
++		cs = NULL;
++	chaininfo = private->hook_entry[hook];
++	nentries = private->hook_entry[hook]->nentries;
++	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+ 	#define cb_base table->private->counters + \
+ 	   cpu_number_map(smp_processor_id()) * table->private->nentries
+-	counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
++	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+ 	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
+ 	// base for chain jumps
+ 	base = (char *)chaininfo;
+@@ -768,10 +772,22 @@
+ 	if (udc_cnt) {
+ 		// this will get free'd in do_replace()/ebt_register_table()
+ 		// if an error occurs
+-		newinfo->chainstack = (struct ebt_chainstack *)
+-		   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++		newinfo->chainstack = (struct ebt_chainstack **)
++		   vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
+ 		if (!newinfo->chainstack)
+ 			return -ENOMEM;
++		for (i = 0; i < smp_num_cpus; i++) {
++			newinfo->chainstack[i] =
++			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++			if (!newinfo->chainstack[i]) {
++				while (i)
++					vfree(newinfo->chainstack[--i]);
++				vfree(newinfo->chainstack);
++				newinfo->chainstack = NULL;
++				break;
++			}
++		}
++
+ 		cl_s = (struct ebt_cl_stack *)
+ 		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+ 		if (!cl_s)
+@@ -825,27 +841,27 @@
+ }
+ 
+ // called under write_lock
+-static inline void get_counters(struct ebt_table_info *info,
+-   struct ebt_counter *counters)
++static inline void get_counters(struct ebt_counter *oldcounters,
++   struct ebt_counter *counters, unsigned int nentries)
+ {
+ 	int i, cpu, counter_base;
+ 
+ 	// counters of cpu 0
+-	memcpy(counters, info->counters,
+-	   sizeof(struct ebt_counter) * info->nentries);
++	memcpy(counters, oldcounters,
++	   sizeof(struct ebt_counter) * nentries);
+ 	// add other counters to those of cpu 0
+ 	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
+-		counter_base = cpu * info->nentries;
+-		for (i = 0; i < info->nentries; i++)
++		counter_base = cpu * nentries;
++		for (i = 0; i < nentries; i++)
+ 			counters[i].pcnt +=
+-			   info->counters[counter_base + i].pcnt;
++			   oldcounters[counter_base + i].pcnt;
+ 	}
+ }
+ 
+ // replace the table
+ static int do_replace(void *user, unsigned int len)
+ {
+-	int ret;
++	int ret, i;
+ 	struct ebt_table_info *newinfo;
+ 	struct ebt_replace tmp;
+ 	struct ebt_table *t;
+@@ -934,7 +950,8 @@
+ 	// we need an atomic snapshot of the counters
+ 	write_lock_bh(&t->lock);
+ 	if (tmp.num_counters)
+-		get_counters(t->private, counterstmp);
++		get_counters(t->private->counters, counterstmp,
++		   t->private->nentries);
+ 
+ 	t->private = newinfo;
+ 	write_unlock_bh(&t->lock);
+@@ -959,8 +976,11 @@
+ 	vfree(table->entries);
+ 	if (table->counters)
+ 		vfree(table->counters);
+-	if (table->chainstack)
++	if (table->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->chainstack[i]);
+ 		vfree(table->chainstack);
++	}
+ 	vfree(table);
+ 
+ 	if (counterstmp)
+@@ -975,8 +995,11 @@
+ 	if (counterstmp)
+ 		vfree(counterstmp);
+ 	// can be initialized in translate_table()
+-	if (newinfo->chainstack)
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
+ 		vfree(newinfo->chainstack);
++	}
+ free_entries:
+ 	if (newinfo->entries)
+ 		vfree(newinfo->entries);
+@@ -1067,7 +1090,7 @@
+ int ebt_register_table(struct ebt_table *table)
+ {
+ 	struct ebt_table_info *newinfo;
+-	int ret;
++	int ret, i;
+ 
+ 	if (!table || !table->table ||!table->table->entries ||
+ 	    table->table->entries_size == 0 ||
+@@ -1135,8 +1158,11 @@
+ free_counters:
+ 	if (newinfo->counters)
+ 		vfree(newinfo->counters);
+-	if (newinfo->chainstack)
++	if (newinfo->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(newinfo->chainstack[i]);
+ 		vfree(newinfo->chainstack);
++	}
+ free_entries:
+ 	vfree(newinfo->entries);
+ free_newinfo:
+@@ -1146,6 +1172,8 @@
+ 
+ void ebt_unregister_table(struct ebt_table *table)
+ {
++	int i;
++
+ 	if (!table) {
+ 		BUGPRINT("Request to unregister NULL table!!!\n");
+ 		return;
+@@ -1159,8 +1187,11 @@
+ 		vfree(table->private->counters);
+ 	if (table->private->entries)
+ 		vfree(table->private->entries);
+-	if (table->private->chainstack)
++	if (table->private->chainstack) {
++		for (i = 0; i < smp_num_cpus; i++)
++			vfree(table->private->chainstack[i]);
+ 		vfree(table->private->chainstack);
++	}
+ 	vfree(table->private);
+ 	MOD_DEC_USE_COUNT;
+ }
+@@ -1263,52 +1294,65 @@
+ }
+ 
+ // called with ebt_mutex down
+-static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++   int *len, int cmd)
+ {
+ 	struct ebt_replace tmp;
+-	struct ebt_table_info *info = t->private;
+-	struct ebt_counter *counterstmp;
+-	int i;
++	struct ebt_counter *counterstmp, *oldcounters;
++	unsigned int entries_size, nentries;
++	char *entries;
++
++	if (cmd == EBT_SO_GET_ENTRIES) {
++		entries_size = t->private->entries_size;
++		nentries = t->private->nentries;
++		entries = t->private->entries;
++		oldcounters = t->private->counters;
++	} else {
++		entries_size = t->table->entries_size;
++		nentries = t->table->nentries;
++		entries = t->table->entries;
++		oldcounters = t->table->counters;
++	}
+ 
+ 	if (copy_from_user(&tmp, user, sizeof(tmp))) {
+ 		BUGPRINT("Cfu didn't work\n");
+ 		return -EFAULT;
+ 	}
+ 
+-	if (*len != sizeof(struct ebt_replace) + info->entries_size +
+-	   (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++	if (*len != sizeof(struct ebt_replace) + entries_size +
++	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
+ 		BUGPRINT("Wrong size\n");
+ 		return -EINVAL;
+ 	}
+ 
+-	if (tmp.nentries != info->nentries) {
++	if (tmp.nentries != nentries) {
+ 		BUGPRINT("Nentries wrong\n");
+ 		return -EINVAL;
+ 	}
+ 
+-	if (tmp.entries_size != info->entries_size) {
++	if (tmp.entries_size != entries_size) {
+ 		BUGPRINT("Wrong size\n");
+ 		return -EINVAL;
+ 	}
+ 
+ 	// userspace might not need the counters
+ 	if (tmp.num_counters) {
+-		if (tmp.num_counters != info->nentries) {
++		if (tmp.num_counters != nentries) {
+ 			BUGPRINT("Num_counters wrong\n");
+ 			return -EINVAL;
+ 		}
+ 		counterstmp = (struct ebt_counter *)
+-		   vmalloc(info->nentries * sizeof(struct ebt_counter));
++		   vmalloc(nentries * sizeof(struct ebt_counter));
+ 		if (!counterstmp) {
+ 			BUGPRINT("Couldn't copy counters, out of memory\n");
+ 			return -ENOMEM;
+ 		}
+ 		write_lock_bh(&t->lock);
+-		get_counters(info, counterstmp);
++		get_counters(oldcounters, counterstmp, nentries);
+ 		write_unlock_bh(&t->lock);
+ 
+ 		if (copy_to_user(tmp.counters, counterstmp,
+-		   info->nentries * sizeof(struct ebt_counter))) {
++		   nentries * sizeof(struct ebt_counter))) {
+ 			BUGPRINT("Couldn't copy counters to userspace\n");
+ 			vfree(counterstmp);
+ 			return -EFAULT;
+@@ -1316,23 +1360,17 @@
+ 		vfree(counterstmp);
+ 	}
+ 
+-	if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++	if (copy_to_user(tmp.entries, entries, entries_size)) {
+ 		BUGPRINT("Couldn't copy entries to userspace\n");
+ 		return -EFAULT;
+ 	}
+-	// make userspace's life easier
+-	memcpy(tmp.hook_entry, info->hook_entry,
+-	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
+-	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+-		tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
+-		   (info->hook_entry[i])) - info->entries + tmp.entries);
+ 	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
+ 		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
+ 		return -EFAULT;
+ 	}
+ 	// set the match/watcher/target names right
+-	return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
+-	   ebt_make_names, info->entries, tmp.entries);
++	return EBT_ENTRY_ITERATE(entries, entries_size,
++	   ebt_make_names, entries, tmp.entries);
+ }
+ 
+ static int do_ebt_set_ctl(struct sock *sk,
+@@ -1368,15 +1406,21 @@
+ 
+ 	switch(cmd) {
+ 	case EBT_SO_GET_INFO:
++	case EBT_SO_GET_INIT_INFO:
+ 		if (*len != sizeof(struct ebt_replace)){
+ 			ret = -EINVAL;
+ 			up(&ebt_mutex);
+ 			break;
+ 		}
+-		tmp.nentries = t->private->nentries;
+-		tmp.entries_size = t->private->entries_size;
+-		// userspace needs this to check the chain names
+-		tmp.valid_hooks = t->valid_hooks;
++		if (cmd == EBT_SO_GET_INFO) {
++			tmp.nentries = t->private->nentries;
++			tmp.entries_size = t->private->entries_size;
++			tmp.valid_hooks = t->valid_hooks;
++		} else {
++			tmp.nentries = t->table->nentries;
++			tmp.entries_size = t->table->entries_size;
++			tmp.valid_hooks = t->table->valid_hooks;
++		}
+ 		up(&ebt_mutex);
+ 		if (copy_to_user(user, &tmp, *len) != 0){
+ 			BUGPRINT("c2u Didn't work\n");
+@@ -1387,9 +1431,10 @@
+ 		break;
+ 
+ 	case EBT_SO_GET_ENTRIES:
+-		ret = copy_everything_to_user(t, user, len);
++	case EBT_SO_GET_INIT_ENTRIES:
++		ret = copy_everything_to_user(t, user, len, cmd);
+ 		up(&ebt_mutex);
+-		break;			
++		break;
+ 
+ 	default:
+ 		up(&ebt_mutex);
+--- linux/include/linux/netfilter_bridge/ebtables.h	Tue Jul 16 18:49:15 2002
++++ ebt2.0pre11.001/include/linux/netfilter_bridge/ebtables.h	Sun Jul 14 19:19:09 2002
+@@ -29,7 +29,9 @@
+ 
+ #define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+ #define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+-#define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+ 
+ // verdicts >0 are "branches"
+ #define EBT_ACCEPT   -1
+@@ -55,9 +57,9 @@
+ 	// one standard (accept, drop, return) per hook
+ 	int policy;
+ 	// nr. of entries
+-	__u32 nentries;
++	unsigned int nentries;
+ 	// entry list
+-	__u8 data[0];
++	char data[0];
+ };
+ 
+ // used for the bitmask of struct ebt_entry
+@@ -238,7 +240,7 @@
+ 	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+ 	struct ebt_counter *counters;
+ 	// room to maintain the stack used for jumping from and into udc
+-	struct ebt_chainstack *chainstack;
++	struct ebt_chainstack **chainstack;
+ 	char *entries;
+ };
+ 
+--- linux/include/linux/netfilter_bridge/ebt_nat.h	Tue Jul 16 18:49:15 2002
++++ ebt2.0pre11.001/include/linux/netfilter_bridge/ebt_nat.h	Tue Jul 16 18:45:16 2002
+@@ -4,7 +4,7 @@
+ struct ebt_nat_info
+ {
+ 	unsigned char mac[ETH_ALEN];
+-	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	// EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
+ 	int target;
+ };
+ #define EBT_SNAT_TARGET "snat"
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff
new file mode 100644
index 0000000..ceeabe1
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff
@@ -0,0 +1,11 @@
+--- linux/include/linux/netfilter_bridge/ebtables.h	Wed Apr 10 19:44:56 2002
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebtables.h	Wed Apr 10 19:30:22 2002
+@@ -108,7 +108,7 @@
+ #define EBT_STANDARD_TARGET "standard"
+ struct ebt_standard_target
+ {
+-	struct ebt_entry_target;
++	struct ebt_entry_target target;
+ 	__u8 verdict;
+ };
+ 
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff
new file mode 100644
index 0000000..db77c43
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff
@@ -0,0 +1,166 @@
+Make size members of ebt_entry_* denote the size of the actual data.
+This makes ebt_do_table() go faster.
+19 April 2002
+
+--- linux/net/bridge/netfilter/ebtables.c	Fri Apr 19 20:47:12 2002
++++ ebt2.0pre3.001/net/bridge/netfilter/ebtables.c	Fri Apr 19 20:42:50 2002
+@@ -65,7 +65,7 @@
+ 	    const struct ebt_counter *c)
+ {
+ 	w->u.watcher->watcher(skb, in, out, w->data,
+-	   w->watcher_size - sizeof(struct ebt_entry_watcher), c);
++	   w->watcher_size, c);
+ 	// watchers don't give a verdict
+ 	return 0;
+ }
+@@ -77,7 +77,7 @@
+ 	    const struct ebt_counter *c)
+ {
+ 	return m->u.match->match(skb, in, out, m->data,
+-	   m->match_size - sizeof(struct ebt_entry_match), c);
++	   m->match_size, c);
+ }
+ 
+ static inline int ebt_dev_check(char *entry, const struct net_device *device)
+@@ -197,7 +197,7 @@
+ 	m->u.match = match;
+ 	if (match->check &&
+ 	   match->check(name, hook, e, m->data,
+-	   m->match_size - sizeof(*m)) != 0) {
++	   m->match_size) != 0) {
+ 		BUGPRINT("match->check failed\n");
+ 		up(&ebt_mutex);
+ 		return -EINVAL;
+@@ -228,7 +228,7 @@
+ 	w->u.watcher = watcher;
+ 	if (watcher->check &&
+ 	   watcher->check(name, hook, e, w->data,
+-	   w->watcher_size - sizeof(*w)) != 0) {
++	   w->watcher_size) != 0) {
+ 		BUGPRINT("watcher->check failed\n");
+ 		up(&ebt_mutex);
+ 		return -EINVAL;
+@@ -318,7 +318,7 @@
+ 	if (i && (*i)-- == 0)
+ 		return 1;
+ 	if (m->u.match->destroy)
+-		m->u.match->destroy(m->data, m->match_size - sizeof(*m));
++		m->u.match->destroy(m->data, m->match_size);
+ 	if (m->u.match->me)
+ 		__MOD_DEC_USE_COUNT(m->u.match->me);
+ 
+@@ -331,7 +331,7 @@
+ 	if (i && (*i)-- == 0)
+ 		return 1;
+ 	if (w->u.watcher->destroy)
+-		w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w));
++		w->u.watcher->destroy(w->data, w->watcher_size);
+ 	if (w->u.watcher->me)
+ 		__MOD_DEC_USE_COUNT(w->u.watcher->me);
+ 
+@@ -411,7 +411,7 @@
+ 		}
+ 	} else if (t->u.target->check &&
+ 	   t->u.target->check(name, hook, e, t->data,
+-	   t->target_size - sizeof(*t)) != 0) {
++	   t->target_size) != 0) {
+ 		if (t->u.target->me)
+ 			__MOD_DEC_USE_COUNT(t->u.target->me);
+ 		ret = -EFAULT;
+@@ -440,7 +440,7 @@
+ 	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ 	if (t->u.target->destroy)
+-		t->u.target->destroy(t->data, t->target_size - sizeof(*t));
++		t->u.target->destroy(t->data, t->target_size);
+ 	if (t->u.target->me)
+ 		__MOD_DEC_USE_COUNT(t->u.target->me);
+ 
+--- linux/include/linux/netfilter_bridge/ebtables.h	Fri Apr 19 20:47:12 2002
++++ ebt2.0pre3.001/include/linux/netfilter_bridge/ebtables.h	Fri Apr 19 20:50:24 2002
+@@ -19,7 +19,7 @@
+ #define EBT_TABLE_MAXNAMELEN 32
+ #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ 
+-/* [gs]etsockopt numbers */
++// [gs]etsockopt numbers
+ #define EBT_BASE_CTL            128
+ 
+ #define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+@@ -84,7 +84,7 @@
+ 		char name[EBT_FUNCTION_MAXNAMELEN];
+ 		struct ebt_match *match;
+ 	} u;
+-	// size of this struct + size of data
++	// size of data
+ 	unsigned int match_size;
+ 	unsigned char data[0];
+ };
+@@ -95,7 +95,7 @@
+ 		char name[EBT_FUNCTION_MAXNAMELEN];
+ 		struct ebt_watcher *watcher;
+ 	} u;
+-	// size of this struct + size of data
++	// size of data
+ 	unsigned int watcher_size;
+ 	unsigned char data[0];
+ };
+@@ -106,7 +106,7 @@
+ 		char name[EBT_FUNCTION_MAXNAMELEN];
+ 		struct ebt_target *target;
+ 	} u;
+-	// size of this struct + size of data
++	// size of data
+ 	unsigned int target_size;
+ 	unsigned char data[0];
+ };
+@@ -118,7 +118,7 @@
+ 	__u8 verdict;
+ };
+ 
+-/* one entry */
++// one entry
+ struct ebt_entry {
+ 	// this needs to be the first field
+ 	__u32 bitmask;
+@@ -199,8 +199,8 @@
+ 	       const void *targetdata,
+ 	       unsigned int datalen);
+ 	// 0 == let it in
+-	int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
+-	       void *targetdata, unsigned int datalen);
++	int (*check)(const char *tablename, unsigned int hooknr,
++	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+ 	void (*destroy)(void *targetdata, unsigned int datalen);
+ 	struct module *me;
+ };
+@@ -227,7 +227,8 @@
+ 	rwlock_t lock;
+ 	// e.g. could be the table explicitly only allows certain
+ 	// matches, targets, ... 0 == let it in
+-	int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks);
++	int (*check)(const struct ebt_table_info *info,
++	   unsigned int valid_hooks);
+ 	// the data used by the kernel
+ 	struct ebt_table_info *private;
+ };
+@@ -256,7 +257,8 @@
+ 	                                                    \
+ 	for (__i = sizeof(struct ebt_entry);                \
+ 	     __i < (e)->watchers_offset;                    \
+-	     __i += __match->match_size) {                  \
++	     __i += __match->match_size +                   \
++	     sizeof(struct ebt_entry_match)) {              \
+ 		__match = (void *)(e) + __i;                \
+ 		                                            \
+ 		__ret = fn(__match , ## args);              \
+@@ -278,7 +280,8 @@
+ 	                                                    \
+ 	for (__i = e->watchers_offset;                      \
+ 	     __i < (e)->target_offset;                      \
+-	     __i += __watcher->watcher_size) {              \
++	     __i += __watcher->watcher_size +               \
++	     sizeof(struct ebt_entry_watcher)) {            \
+ 		__watcher = (void *)(e) + __i;              \
+ 		                                            \
+ 		__ret = fn(__watcher , ## args);            \
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff
new file mode 100644
index 0000000..7fb0399
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff
@@ -0,0 +1,66 @@
+* Add logical bridge in/out device filtering support
+* Be more paranoid about the given userspace device names
+
+--- linux/net/bridge/netfilter/ebtables.c	Fri Apr 19 21:48:59 2002
++++ ebt2.0pre3.002/net/bridge/netfilter/ebtables.c	Fri Apr 19 23:21:22 2002
+@@ -30,6 +30,8 @@
+ #include <asm/uaccess.h>
+ #include <linux/smp.h>
+ #include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
+ 
+ // list_named_find
+ #define ASSERT_READ_LOCK(x)
+@@ -115,6 +117,11 @@
+ 		      (point->bitmask & EBT_802_3), EBT_IPROTO) )
+ 		   && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
+ 		   && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++		   && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++		      (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++		   && ((!out || !out->br_port) ? 1 :
++		       FWINV(!ebt_dev_check((char *)
++		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+ 		) {
+ 			if ( (point->bitmask & EBT_SOURCEMAC) &&
+ 			   FWINV(!!memcmp(point->sourcemac,
+@@ -363,6 +370,10 @@
+ 		BUGPRINT("NOPROTO & 802_3 not allowed\n");
+ 		return -EINVAL;
+ 	}
++	e->in[IFNAMSIZ - 1] = '\0';
++	e->out[IFNAMSIZ - 1] = '\0';
++	e->logical_in[IFNAMSIZ - 1] = '\0';
++	e->logical_out[IFNAMSIZ - 1] = '\0';
+ 	// what hook do we belong to?
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if ((valid_hooks & (1 << i)) == 0)
+--- linux/include/linux/netfilter_bridge/ebtables.h	Fri Apr 19 21:48:59 2002
++++ ebt2.0pre3.002/include/linux/netfilter_bridge/ebtables.h	Fri Apr 19 21:06:25 2002
+@@ -71,7 +71,10 @@
+ #define EBT_IOUT 0x04
+ #define EBT_ISOURCE 0x8
+ #define EBT_IDEST 0x10
+-#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST)
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+ 
+ struct ebt_counter
+ {
+@@ -124,8 +127,14 @@
+ 	__u32 bitmask;
+ 	__u32 invflags;
+ 	__u16 ethproto;
++	// the physical in-dev
+ 	__u8 in[IFNAMSIZ];
++	// the logical in-dev
++	__u8 logical_in[IFNAMSIZ];
++	// the physical out-dev
+ 	__u8 out[IFNAMSIZ];
++	// the logical out-dev
++	__u8 logical_out[IFNAMSIZ];
+ 	__u8 sourcemac[ETH_ALEN];
+ 	__u8 destmac[ETH_ALEN];
+ 	// sizeof ebt_entry + matches
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff
new file mode 100644
index 0000000..375d347
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff
@@ -0,0 +1,367 @@
+April 22
+* add brouter support
+* add --[d,s]nat-target option kernel support for the nat target
+
+--- linux/net/bridge/br_private.h	Mon Apr 22 23:03:27 2002
++++ ebt2.0pre3.003/net/bridge/br_private.h	Mon Apr 22 22:44:20 2002
+@@ -170,7 +170,7 @@
+ 
+ /* br_input.c */
+ extern int  br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+ 
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3.003/net/bridge/netfilter/ebtable_broute.c	Mon Apr 22 19:11:31 2002
+@@ -0,0 +1,80 @@
++/*
++ *  ebtable_broute
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  This table lets you choose between routing and bridging for frames
++ *  entering on a bridge enslaved nic. This table is traversed before any
++ *  other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++  {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++  "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++  { [NF_BR_BROUTING]&initial_chain}, {},
++  0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++	if (valid_hooks & ~(1 << NF_BR_BROUTING))
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_table broute_table =
++{
++  {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++  RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *))
++{
++	return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++	int ret;
++
++	ret = ebt_register_table(&broute_table);
++	if (ret < 0)
++		return ret;
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	// in br_input.c, br_handle_frame() wants to call broute_decision()
++	broute_decision = ebt_broute;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	return ret;
++}
++
++static void __exit fini(void)
++{
++	br_write_lock_bh(BR_NETPROTO_LOCK);
++	broute_decision = NULL;
++	br_write_unlock_bh(BR_NETPROTO_LOCK);
++	ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- linux/include/linux/if_bridge.h	Thu Nov 22 20:47:12 2001
++++ ebt2.0pre3.003/include/linux/if_bridge.h	Mon Apr 22 19:29:41 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+ 
+ #endif
+--- linux/net/core/dev.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3.003/net/core/dev.c	Sun Apr 21 18:15:38 2002
+@@ -1384,7 +1384,14 @@
+ }
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++			const struct net_device *in,
++			const struct net_device *out,
++			int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+ 
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+ 
+ 	if (pt_prev) {
+ 		if (!pt_prev->data)
+-			ret = deliver_to_old_ones(pt_prev, skb, 0);
++			deliver_to_old_ones(pt_prev, skb, 0);
+ 		else {
+ 			atomic_inc(&skb->users);
+-			ret = pt_prev->func(skb, skb->dev, pt_prev);
++			pt_prev->func(skb, skb->dev, pt_prev);
+ 		}
+ 	}
+ 
+-	br_handle_frame_hook(skb);
++	ret = br_handle_frame_hook(skb);
+ 	return ret;
+ }
+ 
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ 			if (skb->dev->br_port != NULL &&
+ 			    br_handle_frame_hook != NULL) {
+-				handle_bridge(skb, pt_prev);
+-				dev_put(rx_dev);
+-				continue;
++				if (handle_bridge(skb, pt_prev) == 0) {
++					dev_put(rx_dev);
++					continue;
++				}
+ 			}
+ #endif
+ 
+--- linux/net/bridge/br_input.c	Mon Apr 22 23:03:27 2002
++++ ebt2.0pre3.003/net/bridge/br_input.c	Sun Apr 21 18:15:23 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ 	return 0;
+ }
+ 
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ 	struct net_bridge *br;
+ 	unsigned char *dest;
+@@ -146,23 +149,30 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++		   skb->dev, NULL, NULL) == NF_DROP)
++			return -1;
++#endif
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+-		return;
++		return 0;
+ 	}
+ 
+ err:
+ 	read_unlock(&br->lock);
+ err_nolock:
+ 	kfree_skb(skb);
+-	return;
++	return 0;
+ 
+ handle_special_frame:
+ 	if (!dest[5]) {
+ 		br_stp_handle_bpdu(skb);
+-		return;
++		return 0;
+ 	}
+ 
+ 	kfree_skb(skb);
++	return 0;
+ }
+--- linux/net/netsyms.c	Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3.003/net/netsyms.c	Sun Apr 21 18:15:56 2002
+@@ -228,6 +228,10 @@
+ 
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h	Tue Jun 12 04:15:27 2001
++++ ebt2.0pre3.003/include/linux/netfilter_bridge.h	Sun Apr 21 19:02:02 2002
+@@ -18,7 +18,8 @@
+ #define NF_BR_LOCAL_OUT		3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING	4
+-#define NF_BR_NUMHOOKS		5
+-
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING		5
++#define NF_BR_NUMHOOKS		6
+ 
+ #endif
+--- linux/net/bridge/netfilter/Makefile	Mon Apr 22 23:03:37 2002
++++ ebt2.0pre3.003/net/bridge/netfilter/Makefile	Sun Apr 21 14:17:32 2002
+@@ -14,6 +14,7 @@
+ obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+ obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+ obj-$(CONFIG_BRIDGE_DB) += br_db.o
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+--- linux/net/bridge/netfilter/Config.in	Mon Apr 22 23:03:37 2002
++++ ebt2.0pre3.003/net/bridge/netfilter/Config.in	Sat Apr 20 18:08:53 2002
+@@ -4,6 +4,7 @@
+ dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
+ dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+--- linux/net/bridge/netfilter/ebtable_nat.c	Mon Apr 22 23:03:37 2002
++++ ebt2.0pre3.003/net/bridge/netfilter/ebtable_nat.c	Sat Apr 20 17:34:35 2002
+@@ -55,18 +55,16 @@
+ 
+ static unsigned int
+ ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+ 
+ // let snat know this frame is routed
+ static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
+ {
+ 	(*pskb)->physindev = NULL;
+ 	return NF_ACCEPT;
+@@ -74,9 +72,8 @@
+ 
+ // let snat know this frame is bridged
+ static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++   const struct net_device *in, const struct net_device *out,
++   int (*okfn)(struct sk_buff *))
+ {
+ 	(*pskb)->physindev = &__fake_net_device;
+ 	return NF_ACCEPT;
+--- linux/net/bridge/netfilter/ebt_nat.c	Mon Apr 22 23:03:37 2002
++++ ebt2.0pre3.003/net/bridge/netfilter/ebt_nat.c	Mon Apr 22 22:48:15 2002
+@@ -33,7 +33,7 @@
+ 	}
+ 	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return EBT_ACCEPT;
++	return infostuff->target;
+ }
+ 
+ __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+@@ -54,29 +54,37 @@
+ 	}
+ 	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return EBT_ACCEPT;
++	return infostuff->target;
+ }
+ 
+ int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
+ 	if (strcmp(tablename, "nat"))
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+ 	if (hooknr != NF_BR_POST_ROUTING)
+ 		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
+ 	return 0;
+ }
+ 
+ int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
+ 	if (strcmp(tablename, "nat"))
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+ 	if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux/include/linux/netfilter_bridge/ebt_nat.h	Mon Apr 22 23:03:37 2002
++++ ebt2.0pre3.003/include/linux/netfilter_bridge/ebt_nat.h	Mon Apr 22 20:43:40 2002
+@@ -4,6 +4,8 @@
+ struct ebt_nat_info
+ {
+ 	unsigned char mac[ETH_ALEN];
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
+ };
+ #define EBT_SNAT_TARGET "snat"
+ #define EBT_DNAT_TARGET "dnat"
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff
new file mode 100644
index 0000000..6ff7f33
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff
@@ -0,0 +1,252 @@
+* add redirect target
+* remove some bugs from nat target
+* use NF_BR_PRI_*
+
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3.004/net/bridge/netfilter/ebt_redirect.c	Sat Apr 27 13:09:16 2002
+@@ -0,0 +1,63 @@
++/*
++ *  ebt_redirect
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	memcpy((**pskb).mac.ethernet->h_dest, in->dev_addr, ETH_ALEN);
++	(*pskb)->pkt_type = PACKET_HOST;
++	return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++	{NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++	ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- linux/include/linux/netfilter_bridge.h	Sun Apr 21 19:02:02 2002
++++ ebt2.0pre3.004/include/linux/netfilter_bridge.h	Sat Apr 27 17:40:09 2002
+@@ -22,4 +22,15 @@
+ #define NF_BR_BROUTING		5
+ #define NF_BR_NUMHOOKS		6
+ 
++enum nf_br_hook_priorities {
++        NF_BR_PRI_FIRST = INT_MIN,
++        NF_BR_PRI_FILTER_BRIDGED = -200,
++        NF_BR_PRI_FILTER_OTHER = 200,
++        NF_BR_PRI_NAT_DST_BRIDGED = -300,
++        NF_BR_PRI_NAT_DST_OTHER = 100,
++        NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++        NF_BR_PRI_NAT_SRC_OTHER = 300,
++        NF_BR_PRI_LAST = INT_MAX,
++};
++
+ #endif
+--- linux/net/bridge/netfilter/Makefile	Sun Apr 21 14:17:32 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/Makefile	Tue Apr 23 22:52:25 2002
+@@ -20,5 +20,5 @@
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+ obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
+-
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+ include $(TOPDIR)/Rules.make
+--- linux/net/bridge/netfilter/Config.in	Sat Apr 20 18:08:53 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/Config.in	Tue Apr 23 22:51:38 2002
+@@ -9,5 +9,6 @@
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+ dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+ 
+--- linux/net/bridge/netfilter/ebtable_filter.c	Sat Apr 13 21:51:47 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/ebtable_filter.c	Sat Apr 27 17:44:20 2002
+@@ -52,9 +52,12 @@
+ }
+ 
+ static struct nf_hook_ops ebt_ops_filter[] = {
+-  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200},
+-  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200},
+-  { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200}
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_BRIDGED},
++	{ { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER}
+ };
+ 
+ static int __init init(void)
+--- linux/net/bridge/netfilter/ebtable_nat.c	Sat Apr 20 17:34:35 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/ebtable_nat.c	Sat Apr 27 17:42:28 2002
+@@ -109,12 +109,18 @@
+ }
+ 
+ static struct nf_hook_ops ebt_ops_nat[] = {
+-	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100},
+-	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100},
+-	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,300},
+-	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300},
+-	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,200 + 1},
+-	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1}
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_NAT_DST_OTHER},
++	{ { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_BRIDGED},
++	{ { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++	   NF_BR_PRI_NAT_SRC_OTHER},
++	{ { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++	   NF_BR_PRI_NAT_DST_BRIDGED},
++	{ { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++	   NF_BR_PRI_FILTER_OTHER + 1},
++	{ { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++	   NF_BR_PRI_FILTER_OTHER + 1}
+ };
+ 
+ static int __init init(void)
+--- linux/net/bridge/netfilter/ebt_nat.c	Mon Apr 22 22:48:15 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/ebt_nat.c	Thu Apr 25 18:49:14 2002
+@@ -15,49 +15,29 @@
+ #include <linux/module.h>
+ #include <net/sock.h>
+ 
+-__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
+-	if (skb_cloned(*pskb)) {
+-		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
+-
+-		if (!nskb)
+-			return EBT_DROP;
+-		if ((*pskb)->sk)
+-			skb_set_owner_w(nskb, (*pskb)->sk);
+-		kfree_skb(*pskb);
+-		*pskb = nskb;
+-	}
+ 	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+ 	return infostuff->target;
+ }
+ 
+-__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
+-	if (skb_cloned(*pskb)) {
+-		struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
+-
+-		if (!nskb)
+-			return EBT_DROP;
+-		if ((*pskb)->sk)
+-			skb_set_owner_w(nskb, (*pskb)->sk);
+-		kfree_skb(*pskb);
+-		*pskb = nskb;
+-	}
+ 	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+ 	return infostuff->target;
+ }
+ 
+-int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+@@ -73,29 +53,29 @@
+ 	return 0;
+ }
+ 
+-int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
+-	if (strcmp(tablename, "nat"))
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
+-		return -EINVAL;
+ 	if (infostuff->target >= NUM_STANDARD_TARGETS)
+ 		return -EINVAL;
+ 	return 0;
+ }
+ 
+-struct ebt_target snat =
++static struct ebt_target snat =
+ {
+ 	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
+ 	NULL, THIS_MODULE
+ };
+ 
+-struct ebt_target dnat =
++static struct ebt_target dnat =
+ {
+ 	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
+ 	NULL, THIS_MODULE
+--- linux/net/bridge/netfilter/ebtables.c	Sat Apr 20 14:05:07 2002
++++ ebt2.0pre3.004/net/bridge/netfilter/ebtables.c	Sat Apr 27 18:10:53 2002
+@@ -122,6 +122,7 @@
+ 		   && ((!out || !out->br_port) ? 1 :
+ 		       FWINV(!ebt_dev_check((char *)
+ 		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
+ 		) {
+ 			if ( (point->bitmask & EBT_SOURCEMAC) &&
+ 			   FWINV(!!memcmp(point->sourcemac,
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff
new file mode 100644
index 0000000..425bc53
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff
@@ -0,0 +1,22 @@
+make redirect work for bridged traffic too
+
+--- ebt2.0pre3.004/net/bridge/netfilter/ebt_redirect.c	Sat Apr 27 13:09:16 2002
++++ ebt2.0pre3.005/net/bridge/netfilter/ebt_redirect.c	Sat Apr 27 22:48:52 2002
+@@ -14,6 +14,7 @@
+ #include <linux/skbuff.h>
+ #include <linux/module.h>
+ #include <net/sock.h>
++#include "../br_private.h"
+ 
+ static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+@@ -21,7 +22,8 @@
+ {
+ 	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+ 
+-	memcpy((**pskb).mac.ethernet->h_dest, in->dev_addr, ETH_ALEN);
++	memcpy((**pskb).mac.ethernet->h_dest,
++	   in->br_port->br->dev.dev_addr, ETH_ALEN);
+ 	(*pskb)->pkt_type = PACKET_HOST;
+ 	return infostuff->target;
+ }
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff
new file mode 100644
index 0000000..3699bdf
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff
@@ -0,0 +1,14 @@
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_redirect.h	Mon Apr 29 20:00:05 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++	__u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff
new file mode 100644
index 0000000..ba86d00
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff
@@ -0,0 +1,51 @@
+--- linux/net/bridge/netfilter/ebtables.c	Fri May  3 20:37:08 2002
++++ ebt2.0pre5.001/net/bridge/netfilter/ebtables.c	Fri May  3 20:28:46 2002
+@@ -124,17 +124,26 @@
+ 		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+ 
+ 		) {
+-			if ( (point->bitmask & EBT_SOURCEMAC) &&
+-			   FWINV(!!memcmp(point->sourcemac,
+-			   ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
+-			   EBT_ISOURCE) )
+-				goto letscontinue;
++			char hlpmac[6];
++			int j;
+ 
+-			if ( (point->bitmask & EBT_DESTMAC) &&
+-			   FWINV(!!memcmp(point->destmac,
+-			   ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
+-			   EBT_IDEST) )
+-				goto letscontinue;
++			if (point->bitmask & EBT_SOURCEMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_source[j] & point->sourcemsk[j];
++				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++				   ETH_ALEN), EBT_ISOURCE) )
++					goto letscontinue;
++			}
++
++			if (point->bitmask & EBT_DESTMAC) {
++				for (j = 0; j < 6; j++)
++					hlpmac[j] = ((**pskb).mac.ethernet)->
++					   h_dest[j] & point->destmsk[j];
++				if (FWINV(!!memcmp(point->destmac, hlpmac,
++				   ETH_ALEN), EBT_IDEST) )
++					goto letscontinue;
++			}
+ 
+ 			if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
+ 			   out, counter_base + i) != 0)
+--- linux/include/linux/netfilter_bridge/ebtables.h	Fri May  3 20:37:08 2002
++++ ebt2.0pre5.001/include/linux/netfilter_bridge/ebtables.h	Thu May  2 19:01:09 2002
+@@ -136,7 +136,9 @@
+ 	// the logical out-dev
+ 	__u8 logical_out[IFNAMSIZ];
+ 	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
+ 	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
+ 	// sizeof ebt_entry + matches
+ 	__u16 watchers_offset;
+ 	// sizeof ebt_entry + matches + watchers
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff
new file mode 100644
index 0000000..4de7890
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff
@@ -0,0 +1,12 @@
+--- linux/net/Config.in	Mon May 20 12:15:38 2002
++++ ebt2.0pre6/net/Config.in	Mon May 20 11:58:15 2002
+@@ -60,7 +60,9 @@
+    source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+    source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+       bool '  netfilter (firewalling) support' CONFIG_BRIDGE_NF
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff
new file mode 100644
index 0000000..edfe983
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff
@@ -0,0 +1,172 @@
+diff -Naur linux-2.4.18-br-nf-ebt/include/linux/netfilter_bridge/ebt_vlan.h linux/include/linux/netfilter_bridge/ebt_vlan.h
+--- linux-2.4.18-br-nf-ebt/include/linux/netfilter_bridge/ebt_vlan.h	Thu Jan  1 03:00:00 1970
++++ linux/include/linux/netfilter_bridge/ebt_vlan.h	Tue May 21 21:04:15 2002
+@@ -0,0 +1,18 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID	0x01
++#define EBT_VLAN_PRIO	0x02
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++	__u16 id;		/* VLAN ID {1-4095} */
++	__u16 prio;		/* VLAN Priority {0-7} */
++	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++				   bit 2=1 - Pirority arg */
++	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++				   bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Config.in linux/net/bridge/netfilter/Config.in
+--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Config.in	Tue May 28 19:20:41 2002
++++ linux/net/bridge/netfilter/Config.in	Tue May 28 19:22:22 2002
+@@ -8,6 +8,7 @@
+ dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+ dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Makefile linux/net/bridge/netfilter/Makefile
+--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/Makefile	Tue May 28 19:20:41 2002
++++ linux/net/bridge/netfilter/Makefile	Thu May 30 11:21:10 2002
+@@ -18,6 +18,7 @@
+ obj-$(CONFIG_BRIDGE_DB) += br_db.o
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+ obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+ obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
+ obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+diff -Naur linux-2.4.18-br-nf-ebt/net/bridge/netfilter/ebt_vlan.c linux/net/bridge/netfilter/ebt_vlan.c
+--- linux-2.4.18-br-nf-ebt/net/bridge/netfilter/ebt_vlan.c	Thu Jan  1 03:00:00 1970
++++ linux/net/bridge/netfilter/ebt_vlan.c	Wed May 29 11:48:38 2002
+@@ -0,0 +1,124 @@
++/*
++ *  ebt_vlan kernelspace
++ *
++ *      Authors:
++ *      Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *      Nick Fedchik <nick@fedchik.org.ua>
++ *
++ *      May, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++			    const struct net_device *in,
++			    const struct net_device *out,
++			    const void *data,
++			    unsigned int datalen,
++			    const struct ebt_counter *c)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct vlan_ethhdr *vlanethhdr =
++	    (struct vlan_ethhdr *) skb->mac.raw;
++	unsigned short v_id;
++	unsigned short v_prio;
++
++	/*
++	 * Calculate 802.1Q VLAN ID and Priority 
++	 * Reserved one bit (13) for CFI 
++	 */
++	v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
++	v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++
++	/*
++	 * Checking VLANs 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
++		if (!((infostuff->id == v_id)
++		      ^ !!(infostuff->invflags & EBT_VLAN_ID))) 
++		return 1;
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
++				infostuff->id,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * Checking Priority 
++	 */
++	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
++		if (!( (infostuff->prio == v_prio) 
++		     ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) 
++		return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++				(infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
++				infostuff->prio,
++				(unsigned char) infostuff->bitmask);
++	}
++	/*
++	 * rule matched 
++	 */
++	return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++			   const struct ebt_entry *e, void *data,
++			   unsigned int datalen)
++{
++	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++	if (datalen != sizeof (struct ebt_vlan_info))
++		return -EINVAL;
++
++	if (e->ethproto != __constant_htons (ETH_P_8021Q))
++		return -EINVAL;
++
++	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct ebt_match filter_vlan = {
++	{NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++	NULL,
++	THIS_MODULE
++};
++
++static int __init init (void)
++{
++	printk (KERN_INFO
++		"ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++	if (debug)
++		printk (KERN_DEBUG
++			"ebt_vlan: 802.1Q matching debug is on\n");
++	return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++	ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_LICENSE ("GPL");
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre8.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre8.001.diff
new file mode 100644
index 0000000..88fdfe1
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre8.001.diff
@@ -0,0 +1,518 @@
+--- linux/net/bridge/netfilter/Makefile	Thu Jun  6 19:51:14 2002
++++ ebt2.0pre8.001/net/bridge/netfilter/Makefile	Thu Jun  6 19:06:22 2002
+@@ -20,6 +20,7 @@
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+ obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+-obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+ obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+ include $(TOPDIR)/Rules.make
+--- linux/net/bridge/netfilter/Config.in	Thu Jun  6 19:51:14 2002
++++ ebt2.0pre8.001/net/bridge/netfilter/Config.in	Thu Jun  6 19:06:22 2002
+@@ -9,7 +9,8 @@
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+ dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+ 
+--- linux/net/bridge/netfilter/ebt_nat.c	Thu Jun  6 19:51:14 2002
++++ /dev/null	Thu Aug 24 11:00:32 2000
+@@ -1,106 +0,0 @@
+-/*
+- *  ebt_nat
+- *
+- *	Authors:
+- *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+- *
+- *  April, 2002
+- *
+- */
+-
+-#include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge/ebt_nat.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+-#include <linux/module.h>
+-#include <net/sock.h>
+-
+-static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+-   const struct net_device *in, const struct net_device *out,
+-   const void *data, unsigned int datalen)
+-{
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+-
+-	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
+-	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
+-}
+-
+-static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+-   const struct net_device *in, const struct net_device *out,
+-   const void *data, unsigned int datalen)
+-{
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+-
+-	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
+-	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
+-}
+-
+-static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
+-   const struct ebt_entry *e, void *data, unsigned int datalen)
+-{
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+-
+-	if (strcmp(tablename, "nat"))
+-		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_nat_info))
+-		return -EINVAL;
+-	if (hooknr != NF_BR_POST_ROUTING)
+-		return -EINVAL;
+-	if (infostuff->target >= NUM_STANDARD_TARGETS)
+-		return -EINVAL;
+-	return 0;
+-}
+-
+-static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
+-   const struct ebt_entry *e, void *data, unsigned int datalen)
+-{
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+-
+-	if ( (strcmp(tablename, "nat") || 
+-	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
+-	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
+-		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_nat_info))
+-		return -EINVAL;
+-	if (infostuff->target >= NUM_STANDARD_TARGETS)
+-		return -EINVAL;
+-	return 0;
+-}
+-
+-static struct ebt_target snat =
+-{
+-	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
+-	NULL, THIS_MODULE
+-};
+-
+-static struct ebt_target dnat =
+-{
+-	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
+-	NULL, THIS_MODULE
+-};
+-
+-static int __init init(void)
+-{
+-	int ret;
+-	ret = ebt_register_target(&snat);
+-	if (ret != 0)
+-		return ret;
+-	ret = ebt_register_target(&dnat);
+-	if (ret == 0)
+-		return 0;
+-	ebt_unregister_target(&snat);
+-	return ret;
+-}
+-
+-static void __exit fini(void)
+-{
+-	ebt_unregister_target(&snat);
+-	ebt_unregister_target(&dnat);
+-}
+-
+-module_init(init);
+-module_exit(fini);
+-EXPORT_NO_SYMBOLS;
+-MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8.001/net/bridge/netfilter/ebt_snat.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_snat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if (strcmp(tablename, "nat"))
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (hooknr != NF_BR_POST_ROUTING)
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target snat =
++{
++	{NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebt2.0pre8.001/net/bridge/netfilter/ebt_dnat.c	Thu Jun  6 19:06:22 2002
+@@ -0,0 +1,64 @@
++/*
++ *  ebt_dnat
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++   const struct net_device *in, const struct net_device *out,
++   const void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	   ETH_ALEN * sizeof(unsigned char));
++	return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++   const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++	if ( (strcmp(tablename, "nat") || 
++	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++		return -EINVAL;
++	if (datalen != sizeof(struct ebt_nat_info))
++		return -EINVAL;
++	if (infostuff->target >= NUM_STANDARD_TARGETS)
++		return -EINVAL;
++	return 0;
++}
++
++static struct ebt_target dnat =
++{
++	{NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++	NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++	return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++	ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- linux/net/bridge/netfilter/ebtables.c	Thu Jun  6 19:51:15 2002
++++ ebt2.0pre8.001/net/bridge/netfilter/ebtables.c	Thu Jun  6 19:06:22 2002
+@@ -19,6 +19,7 @@
+ #include <linux/sched.h>
+ #include <linux/tty.h>
+ 
++#include <linux/kmod.h>
+ #include <linux/module.h>
+ #include <linux/vmalloc.h>
+ #include <linux/skbuff.h>
+@@ -195,6 +196,76 @@
+ 	return NF_DROP;
+ }
+ 
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++			const char *name,
++			int *error,
++			struct semaphore *mutex)
++{
++	void *ret;
++
++	*error = down_interruptible(mutex);
++	if (*error != 0)
++		return NULL;
++
++	ret = list_named_find(head, name);
++	if (!ret) {
++		*error = -ENOENT;
++		up(mutex);
++	}
++	return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++		 const char *name,
++		 const char *prefix,
++		 int *error,
++		 struct semaphore *mutex)
++{
++	void *ret;
++
++	ret = find_inlist_lock_noload(head, name, error, mutex);
++	if (!ret) {
++		char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++		strcpy(modulename, prefix);
++		strcat(modulename, name);
++		request_module(modulename);
++		ret = find_inlist_lock_noload(head, name, error, mutex);
++	}
++
++	return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++	return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
+ static inline int
+ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+    const char *name, unsigned int hook, unsigned int *cnt)
+@@ -203,25 +274,20 @@
+ 	int ret;
+ 
+ 	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+-	ret = down_interruptible(&ebt_mutex);
+-	if (ret != 0)
+-		return -EFAULT;
+-	if (!(match = (struct ebt_match *)
+-	   list_named_find(&ebt_matches, m->u.name))) {
+-		up(&ebt_mutex);
+-		return -ENOENT;
+-	}
++	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++	if (!match) 
++		return ret;
+ 	m->u.match = match;
++	if (match->me)
++		__MOD_INC_USE_COUNT(match->me);
++	up(&ebt_mutex);
+ 	if (match->check &&
+-	   match->check(name, hook, e, m->data,
+-	   m->match_size) != 0) {
++	   match->check(name, hook, e, m->data, m->match_size) != 0) {
+ 		BUGPRINT("match->check failed\n");
+-		up(&ebt_mutex);
++		if (match->me)
++			__MOD_DEC_USE_COUNT(match->me);
+ 		return -EINVAL;
+ 	}
+-	if (match->me)
+-		__MOD_INC_USE_COUNT(match->me);
+-	up(&ebt_mutex);
+ 	(*cnt)++;
+ 	return 0;
+ }
+@@ -233,26 +299,21 @@
+ 	struct ebt_watcher *watcher;
+ 	int ret;
+ 
+-	ret = down_interruptible(&ebt_mutex);
+-	if (ret != 0)
+-		return -EFAULT;
+ 	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+-	if (!(watcher = (struct ebt_watcher *)
+-	   list_named_find(&ebt_watchers, w->u.name))) {
+-		up(&ebt_mutex);
+-		return -ENOENT;
+-	}
++	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++	if (!watcher) 
++		return ret;
+ 	w->u.watcher = watcher;
++	if (watcher->me)
++		__MOD_INC_USE_COUNT(watcher->me);
++	up(&ebt_mutex);
+ 	if (watcher->check &&
+-	   watcher->check(name, hook, e, w->data,
+-	   w->watcher_size) != 0) {
++	   watcher->check(name, hook, e, w->data, w->watcher_size) != 0) {
+ 		BUGPRINT("watcher->check failed\n");
+-		up(&ebt_mutex);
++		if (watcher->me)
++			__MOD_DEC_USE_COUNT(watcher->me);
+ 		return -EINVAL;
+ 	}
+-	if (watcher->me)
+-		__MOD_INC_USE_COUNT(watcher->me);
+-	up(&ebt_mutex);
+ 	(*cnt)++;
+ 	return 0;
+ }
+@@ -402,16 +463,10 @@
+ 	if (ret != 0)
+ 		goto cleanup_watchers;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-	ret = down_interruptible(&ebt_mutex);
+-	if (ret != 0)
+-		goto cleanup_watchers;
+ 	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+-	if (!(target = (struct ebt_target *)
+-	   list_named_find(&ebt_targets, t->u.name))) {
+-		ret = -ENOENT;
+-		up(&ebt_mutex);
++	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++	if (!target) 
+ 		goto cleanup_watchers;
+-	}
+ 	if (target->me)
+ 		__MOD_INC_USE_COUNT(target->me);
+ 	up(&ebt_mutex);
+@@ -545,8 +600,8 @@
+ 	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ 	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
+ 	if (ret != 0) {
+-		BUGPRINT("ebt_check_entry gave fault back\n");
+-		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_cleanup_entry, &i);
+ 	}
+ 	return ret;
+ }
+@@ -640,17 +695,9 @@
+ 	if (ret != 0)
+ 		goto free_counterstmp;
+ 
+-	ret = down_interruptible(&ebt_mutex);
+-
+-	if (ret != 0)
+-		goto free_cleanup;
+-
+-	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
+-		ret = -ENOENT;
+-		// give some help to the poor user
+-		print_string("The table is not present, try insmod\n");
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
+ 		goto free_unlock;
+-	}
+ 
+ 	// the table doesn't like it
+ 	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+@@ -700,7 +747,6 @@
+ 
+ free_unlock:
+ 	up(&ebt_mutex);
+-free_cleanup:
+ 	EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ 	   ebt_cleanup_entry, NULL);
+ free_counterstmp:
+@@ -912,14 +958,10 @@
+ 	}
+ 
+ 	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+-	ret = down_interruptible(&ebt_mutex);
+-	if (ret != 0)
+-		goto free_tmp;
+ 
+-	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
+-		ret = -EINVAL;
+-		goto unlock_mutex;
+-	}
++	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++	if (!t)
++		goto free_tmp;
+ 
+ 	if (hlp.num_counters != t->private->nentries) {
+ 		BUGPRINT("Wrong nr of counters\n");
+@@ -1092,15 +1134,9 @@
+ 	if (copy_from_user(&tmp, user, sizeof(tmp)))
+ 		return -EFAULT;
+ 
+-	ret = down_interruptible(&ebt_mutex);
+-	if (ret != 0)
++	t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++	if (!t)
+ 		return ret;
+-
+-	if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
+-		print_string("Table not found, try insmod\n");
+-		up(&ebt_mutex);
+-		return -EINVAL;
+-	}
+ 
+ 	switch(cmd) {
+ 	case EBT_SO_GET_INFO:
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff
new file mode 100644
index 0000000..97d526f
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff
@@ -0,0 +1,1128 @@
+--- linux/net/bridge/netfilter/ebtable_filter.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_filter.c	Thu Jun 27 19:11:50 2002
+@@ -17,17 +17,16 @@
+ 
+ static struct ebt_entries initial_chains[] =
+ {
+-  {0, EBT_ACCEPT, 0},
+-  {0, EBT_ACCEPT, 0},
+-  {0, EBT_ACCEPT, 0}
++  {0, "INPUT", 0, EBT_ACCEPT, 0},
++  {0, "FORWARD", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0}
+ };
+ 
+-static struct ebt_replace initial_table = 
+-{ 
++static struct ebt_replace initial_table =
++{
+   "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+   { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
+-    [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
+-  0, NULL, (char *)initial_chains
++    [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+ };
+ 
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebtable_nat.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_nat.c	Thu Jun 27 19:11:50 2002
+@@ -17,17 +17,16 @@
+ 
+ static struct ebt_entries initial_chains[] =
+ {
+-  {0, EBT_ACCEPT, 0},
+-  {0, EBT_ACCEPT, 0},
+-  {0, EBT_ACCEPT, 0}
++  {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++  {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++  {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
+ };
+ 
+ static struct ebt_replace initial_table =
+ {
+   "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+   { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
+-    [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
+-  0, NULL, (char *)initial_chains
++    [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+ };
+ 
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebtable_broute.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_broute.c	Thu Jun 27 19:11:50 2002
+@@ -21,13 +21,12 @@
+ // EBT_ACCEPT means the frame will be bridged
+ // EBT_DROP means the frame will be routed
+ static struct ebt_entries initial_chain =
+-  {0, EBT_ACCEPT, 0};
++  {0, "BROUTE", 0, EBT_ACCEPT, 0};
+ 
+ static struct ebt_replace initial_table =
+ {
+   "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
+-  { [NF_BR_BROUTING]&initial_chain}, {},
+-  0, NULL, (char *)&initial_chain
++  { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
+ };
+ 
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebt_redirect.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_redirect.c	Thu Jun 27 19:11:50 2002
+@@ -16,7 +16,7 @@
+ #include <net/sock.h>
+ #include "../br_private.h"
+ 
+-static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+@@ -28,17 +28,17 @@
+ 	return infostuff->target;
+ }
+ 
+-static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+ 
+-	if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
+-	     (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_redirect_info))
+ 		return -EINVAL;
+-	if (infostuff->target >= NUM_STANDARD_TARGETS)
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux/net/bridge/netfilter/ebt_arp.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_arp.c	Thu Jun 27 19:11:50 2002
+@@ -68,7 +68,7 @@
+ 	return 0;
+ }
+ 
+-static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
+--- linux/net/bridge/netfilter/ebt_ip.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_ip.c	Thu Jun 27 19:11:50 2002
+@@ -39,7 +39,7 @@
+ 	return 0;
+ }
+ 
+-static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
+--- linux/net/bridge/netfilter/ebt_vlan.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_vlan.c	Thu Jun 27 19:11:50 2002
+@@ -5,7 +5,7 @@
+  *      Bart De Schuymer <bart.de.schuymer@pandora.be>
+  *      Nick Fedchik <nick@fedchik.org.ua>
+  *
+- *      May, 2002
++ *      June, 2002
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+@@ -18,6 +18,8 @@
+ MODULE_PARM (debug, "0-1b");
+ MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
+ 
++#define MODULE_VERSION "0.2"
++
+ static int ebt_filter_vlan (const struct sk_buff *skb,
+ 			    const struct net_device *in,
+ 			    const struct net_device *out,
+@@ -30,43 +32,64 @@
+ 	    (struct vlan_ethhdr *) skb->mac.raw;
+ 	unsigned short v_id;
+ 	unsigned short v_prio;
++	unsigned short v_TCI;
+ 
+ 	/*
+-	 * Calculate 802.1Q VLAN ID and Priority 
+-	 * Reserved one bit (13) for CFI 
++	 * Calculate 802.1Q VLAN ID and user_priority from 
++	 * Tag Control Information (TCI) field.
++	 * Reserved one bit (13) for CFI (Canonical Format Indicator)
+ 	 */
+-	v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
+-	v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++	v_TCI = ntohs (vlanethhdr->h_vlan_TCI);
++	v_id = v_TCI & 0xFFF;
++	v_prio = v_TCI >> 13;
+ 
+ 	/*
+ 	 * Checking VLANs 
+ 	 */
+ 	if (infostuff->bitmask & EBT_VLAN_ID) {	/* Is VLAN ID parsed? */
+ 		if (!((infostuff->id == v_id)
+-		      ^ !!(infostuff->invflags & EBT_VLAN_ID))) 
+-		return 1;
++		      ^ !!(infostuff->invflags & EBT_VLAN_ID)))
++			return 1;
+ 		if (debug)
+ 			printk (KERN_DEBUG
+ 				"ebt_vlan: matched ID=%s%d (mask=%X)\n",
+-				(infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
+-				infostuff->id,
+-				(unsigned char) infostuff->bitmask);
++				(infostuff->
++				 invflags & EBT_VLAN_ID) ? "!" : "",
++				infostuff->id, infostuff->bitmask);
+ 	}
+ 	/*
+-	 * Checking Priority 
++	 * Checking User Priority 
+ 	 */
+ 	if (infostuff->bitmask & EBT_VLAN_PRIO) {	/* Is VLAN Prio parsed? */
+-		if (!( (infostuff->prio == v_prio) 
+-		     ^ !!(infostuff->invflags & EBT_VLAN_PRIO))) 
+-		return 1;	/* missed */
++		if (!((infostuff->prio == v_prio)
++		      ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
++			return 1;	/* missed */
+ 		if (debug)
+ 			printk (KERN_DEBUG
+ 				"ebt_vlan: matched Prio=%s%d (mask=%X)\n",
+-				(infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
+-				infostuff->prio,
+-				(unsigned char) infostuff->bitmask);
++				(infostuff->
++				 invflags & EBT_VLAN_PRIO) ? "!" : "",
++				infostuff->prio, infostuff->bitmask);
+ 	}
+ 	/*
++	 * Checking for Encapsulated proto
++	 */
++	if (infostuff->bitmask & EBT_VLAN_ENCAP) {	/* Is VLAN Encap parsed? */
++		if (!
++		    ((infostuff->encap ==
++		      vlanethhdr->h_vlan_encapsulated_proto)
++		     ^ !!(infostuff->invflags & EBT_VLAN_ENCAP)))
++			return 1;	/* missed */
++		if (debug)
++			printk (KERN_DEBUG
++				"ebt_vlan: matched encap=%s%2.4X (mask=%X)\n",
++				(infostuff->
++				 invflags & EBT_VLAN_ENCAP) ? "!" : "",
++				ntohs (infostuff->encap),
++				infostuff->bitmask);
++	}
++
++	/*
+ 	 * rule matched 
+ 	 */
+ 	return 0;
+@@ -76,7 +99,7 @@
+  * ebt_vlan_check() is called when userspace delivers the table to the kernel, 
+  * * it is called to check that userspace doesn't give a bad table.
+  */
+-static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
+ 			   const struct ebt_entry *e, void *data,
+ 			   unsigned int datalen)
+ {
+@@ -96,7 +119,10 @@
+ }
+ 
+ static struct ebt_match filter_vlan = {
+-	{NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++	{NULL, NULL},
++	EBT_VLAN_MATCH,
++	ebt_filter_vlan,
++	ebt_vlan_check,
+ 	NULL,
+ 	THIS_MODULE
+ };
+@@ -104,10 +130,11 @@
+ static int __init init (void)
+ {
+ 	printk (KERN_INFO
+-		"ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++		"ebt_vlan: 802.1Q VLAN matching module for EBTables "
++		MODULE_VERSION "\n");
+ 	if (debug)
+ 		printk (KERN_DEBUG
+-			"ebt_vlan: 802.1Q matching debug is on\n");
++			"ebt_vlan: 802.1Q rule matching debug is on\n");
+ 	return ebt_register_match (&filter_vlan);
+ }
+ 
+@@ -120,5 +147,6 @@
+ module_exit (fini);
+ EXPORT_NO_SYMBOLS;
+ MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
+-MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v"
++		    MODULE_VERSION);
+ MODULE_LICENSE ("GPL");
+--- linux/net/bridge/netfilter/ebt_log.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_log.c	Thu Jun 27 19:11:50 2002
+@@ -17,7 +17,7 @@
+ 
+ static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
+ 
+-static int ebt_log_check(const char *tablename, unsigned int hooknr,
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
+--- linux/net/bridge/netfilter/ebt_snat.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_snat.c	Thu Jun 27 19:11:50 2002
+@@ -15,7 +15,7 @@
+ #include <linux/module.h>
+ #include <net/sock.h>
+ 
+-static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+@@ -26,7 +26,7 @@
+ 	return infostuff->target;
+ }
+ 
+-static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+@@ -35,9 +35,9 @@
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	if (hooknr != NF_BR_POST_ROUTING)
++	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ 		return -EINVAL;
+-	if (infostuff->target >= NUM_STANDARD_TARGETS)
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux/net/bridge/netfilter/ebt_dnat.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_dnat.c	Thu Jun 27 19:11:50 2002
+@@ -15,7 +15,7 @@
+ #include <linux/module.h>
+ #include <net/sock.h>
+ 
+-static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+@@ -26,18 +26,18 @@
+ 	return infostuff->target;
+ }
+ 
+-static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+ 
+-	if ( (strcmp(tablename, "nat") || 
+-	   (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
+-	   (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++	if ( (strcmp(tablename, "nat") ||
++	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	if (infostuff->target >= NUM_STANDARD_TARGETS)
++	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux/net/bridge/netfilter/ebtables.c	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtables.c	Thu Jun 27 19:11:50 2002
+@@ -90,27 +90,35 @@
+ 	if (!device)
+ 		return 1;
+ 	return strncmp(entry, device->name, IFNAMSIZ);
+-}	
++}
+ 
+ // Do some firewalling
+ unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+    const struct net_device *in, const struct net_device *out,
+    struct ebt_table *table)
+ {
+-	int i, nentries;
++	int i, j, nentries;
+ 	struct ebt_entry *point;
+ 	struct ebt_counter *counter_base;
+ 	struct ebt_entry_target *t;
+-	__u8 verdict;
++	int verdict, sp = 0;
++	struct ebt_chainstack *cs;
++	struct ebt_entries *chaininfo;
++	char *base;
+ 
+ 	read_lock_bh(&table->lock);
++	cs = table->private->chainstack;
++	chaininfo = table->private->hook_entry[hook];
+ 	nentries = table->private->hook_entry[hook]->nentries;
+ 	point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
+-	counter_base = table->private->counters +
+-	   cpu_number_map(smp_processor_id()) * table->private->nentries +
+-	   table->private->counter_entry[hook];
++	#define cb_base table->private->counters + \
++	   cpu_number_map(smp_processor_id()) * table->private->nentries
++	counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
+ 	#define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
+- 	for (i = 0; i < nentries; i++) {
++	// base for chain jumps
++	base = (char *)chaininfo;
++	i = 0;
++ 	while (i < nentries) {
+ 		if ( ( point->bitmask & EBT_NOPROTO ||
+ 		   FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
+ 		      EBT_IPROTO)
+@@ -125,24 +133,23 @@
+ 		      (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+ 
+ 		) {
+-			char hlpmac[6];
+-			int j;
+-
+ 			if (point->bitmask & EBT_SOURCEMAC) {
++				verdict = 0;
+ 				for (j = 0; j < 6; j++)
+-					hlpmac[j] = ((**pskb).mac.ethernet)->
+-					   h_source[j] & point->sourcemsk[j];
+-				if (FWINV(!!memcmp(point->sourcemac, hlpmac,
+-				   ETH_ALEN), EBT_ISOURCE) )
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_source[j] ^ point->sourcemac[j]) &
++					   point->sourcemsk[j];
++				if (FWINV(!!verdict, EBT_ISOURCE) )
+ 					goto letscontinue;
+ 			}
+ 
+ 			if (point->bitmask & EBT_DESTMAC) {
++				verdict = 0;
+ 				for (j = 0; j < 6; j++)
+-					hlpmac[j] = ((**pskb).mac.ethernet)->
+-					   h_dest[j] & point->destmsk[j];
+-				if (FWINV(!!memcmp(point->destmac, hlpmac,
+-				   ETH_ALEN), EBT_IDEST) )
++					verdict |= (((**pskb).mac.ethernet)->
++					   h_dest[j] ^ point->destmac[j]) &
++					   point->destmsk[j];
++				if (FWINV(!!verdict, EBT_IDEST) )
+ 					goto letscontinue;
+ 			}
+ 
+@@ -175,20 +182,56 @@
+ 				read_unlock_bh(&table->lock);
+ 				return NF_DROP;
+ 			}
+-			if (verdict != EBT_CONTINUE) {
++			if (verdict == EBT_RETURN) {
++letsreturn:
++				if (sp == 0)
++					// act like this is EBT_CONTINUE
++					goto letscontinue;
++				sp--;
++				// put all the local variables right
++				i = cs[sp].n;
++				chaininfo = cs[sp].chaininfo;
++				nentries = chaininfo->nentries;
++				point = cs[sp].e;
++				counter_base = cb_base +
++				   chaininfo->counter_offset;
++				continue;
++			}
++			if (verdict == EBT_CONTINUE)
++				goto letscontinue;
++			if (verdict < 0) {
++				BUGPRINT("bogus standard verdict\n");
++				read_unlock_bh(&table->lock);
++				return NF_DROP;
++			}
++			// jump to a udc
++			cs[sp].n = i + 1;
++			cs[sp].chaininfo = chaininfo;
++			cs[sp].e = (struct ebt_entry *)
++			   (((char *)point) + point->next_offset);
++			i = 0;
++			chaininfo = (struct ebt_entries *) (base + verdict);
++			if (chaininfo->distinguisher) {
++				BUGPRINT("jump to non-chain\n");
+ 				read_unlock_bh(&table->lock);
+-				BUGPRINT("Illegal target while "
+-				         "firewalling!!\n");
+-				// Try not to get oopsen
+ 				return NF_DROP;
+ 			}
++			nentries = chaininfo->nentries;
++			point = (struct ebt_entry *)chaininfo->data;
++			counter_base = cb_base + chaininfo->counter_offset;
++			sp++;
++			continue;
+ 		}
+ letscontinue:
+ 		point = (struct ebt_entry *)
+ 		   (((char *)point) + point->next_offset);
++		i++;
+ 	}
+ 
+-	if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++	// I actually like this :)
++	if (chaininfo->policy == EBT_RETURN)
++		goto letsreturn;
++	if (chaininfo->policy == EBT_ACCEPT) {
+ 		read_unlock_bh(&table->lock);
+ 		return NF_ACCEPT;
+ 	}
+@@ -268,7 +311,7 @@
+ 
+ static inline int
+ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+-   const char *name, unsigned int hook, unsigned int *cnt)
++   const char *name, unsigned int hookmask, unsigned int *cnt)
+ {
+ 	struct ebt_match *match;
+ 	int ret;
+@@ -282,7 +325,7 @@
+ 		__MOD_INC_USE_COUNT(match->me);
+ 	up(&ebt_mutex);
+ 	if (match->check &&
+-	   match->check(name, hook, e, m->data, m->match_size) != 0) {
++	   match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+ 		BUGPRINT("match->check failed\n");
+ 		if (match->me)
+ 			__MOD_DEC_USE_COUNT(match->me);
+@@ -294,7 +337,7 @@
+ 
+ static inline int
+ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+-   const char *name, unsigned int hook, unsigned int *cnt)
++   const char *name, unsigned int hookmask, unsigned int *cnt)
+ {
+ 	struct ebt_watcher *watcher;
+ 	int ret;
+@@ -308,7 +351,7 @@
+ 		__MOD_INC_USE_COUNT(watcher->me);
+ 	up(&ebt_mutex);
+ 	if (watcher->check &&
+-	   watcher->check(name, hook, e, w->data, w->watcher_size) != 0) {
++	   watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+ 		BUGPRINT("watcher->check failed\n");
+ 		if (watcher->me)
+ 			__MOD_DEC_USE_COUNT(watcher->me);
+@@ -324,7 +367,7 @@
+ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+    struct ebt_table_info *newinfo, char *base, char *limit,
+    struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+-   unsigned int *totalcnt, unsigned int valid_hooks)
++   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+ {
+ 	int i;
+ 
+@@ -336,7 +379,8 @@
+ 			break;
+ 	}
+ 	// beginning of a new chain
+-	if (i != NF_BR_NUMHOOKS) {
++	// if i == NF_BR_NUMHOOKS it must be a user defined chain
++	if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+ 		if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+ 			// we make userspace set this right,
+ 			// so there is no misunderstanding
+@@ -359,13 +403,23 @@
+ 		}
+ 		if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+ 		   ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+-			BUGPRINT("bad policy\n");
++			// only RETURN from udc
++			if (i != NF_BR_NUMHOOKS ||
++			   ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++				BUGPRINT("bad policy\n");
++				return -EINVAL;
++			}
++		}
++		if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++			(*udc_cnt)++;
++		else
++			newinfo->hook_entry[i] = (struct ebt_entries *)e;
++		if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++			BUGPRINT("counter_offset != totalcnt");
+ 			return -EINVAL;
+ 		}
+ 		*n = ((struct ebt_entries *)e)->nentries;
+ 		*cnt = 0;
+-		newinfo->hook_entry[i] = (struct ebt_entries *)e;
+-		newinfo->counter_entry[i] = *totalcnt;
+ 		return 0;
+ 	}
+ 	// a plain old entry, heh
+@@ -375,21 +429,55 @@
+ 		BUGPRINT("entry offsets not in right order\n");
+ 		return -EINVAL;
+ 	}
+-	if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
+-		BUGPRINT("entry offsets point too far\n");
++	// this is not checked anywhere else
++	if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++		BUGPRINT("target size too small\n");
+ 		return -EINVAL;
+ 	}
+ 
+-	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
+-		BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
+-		         "bitmask for an entry\n");
+-		return -EINVAL;
+-	}
+ 	(*cnt)++;
+ 	(*totalcnt)++;
+ 	return 0;
+ }
+ 
++struct ebt_cl_stack
++{
++	struct ebt_chainstack cs;
++	int from;
++	unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++   struct ebt_cl_stack *udc)
++{
++	int i;
++
++	// we're only interested in chain starts
++	if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++		return 0;
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if ((valid_hooks & (1 << i)) == 0)
++			continue;
++		if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++			break;
++	}
++	// only care about udc
++	if (i != NF_BR_NUMHOOKS)
++		return 0;
++
++	udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++	// these initialisations are depended on later in check_chainloops()
++	udc[*n].cs.n = 0;
++	udc[*n].hookmask = 0;
++
++	(*n)++;
++	return 0;
++}
++
+ static inline int
+ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ {
+@@ -418,11 +506,12 @@
+ 
+ static inline int
+ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+-   const char *name, unsigned int *cnt, unsigned int valid_hooks)
++   const char *name, unsigned int *cnt, unsigned int valid_hooks,
++   struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+ {
+ 	struct ebt_entry_target *t;
+ 	struct ebt_target *target;
+-	unsigned int i, j, hook = 0;
++	unsigned int i, j, hook = 0, hookmask = 0;
+ 	int ret;
+ 
+ 	// Don't mess with the struct ebt_entries
+@@ -454,18 +543,29 @@
+ 		else
+ 			break;
+ 	}
++	if (i < NF_BR_NUMHOOKS)
++		hookmask = (1 << hook);
++	else {
++		for (i = 0; i < udc_cnt; i++)
++			if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++				break;
++		if (i == 0)
++			hookmask = (1 << hook);
++		else
++			hookmask = cl_s[i - 1].hookmask;
++	}
+ 	i = 0;
+-	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++	ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+ 	if (ret != 0)
+ 		goto cleanup_matches;
+ 	j = 0;
+-	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++	ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+ 	if (ret != 0)
+ 		goto cleanup_watchers;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ 	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+-	if (!target) 
++	if (!target)
+ 		goto cleanup_watchers;
+ 	if (target->me)
+ 		__MOD_INC_USE_COUNT(target->me);
+@@ -479,14 +579,14 @@
+ 			ret = -EFAULT;
+ 			goto cleanup_watchers;
+ 		}
+-		if (((struct ebt_standard_target *)t)->verdict >=
+-		   NUM_STANDARD_TARGETS) {
++		if (((struct ebt_standard_target *)t)->verdict <
++		   -NUM_STANDARD_TARGETS) {
+ 			BUGPRINT("Invalid standard target\n");
+ 			ret = -EFAULT;
+ 			goto cleanup_watchers;
+ 		}
+ 	} else if (t->u.target->check &&
+-	   t->u.target->check(name, hook, e, t->data,
++	   t->u.target->check(name, hookmask, e, t->data,
+ 	   t->target_size) != 0) {
+ 		if (t->u.target->me)
+ 			__MOD_DEC_USE_COUNT(t->u.target->me);
+@@ -523,12 +623,83 @@
+ 	return 0;
+ }
+ 
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++   unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++	int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++	struct ebt_entry *e = (struct ebt_entry *)chain->data;
++	struct ebt_entry_target *t;
++
++	while (pos < nentries || chain_nr != -1) {
++		// end of udc, go back one 'recursion' step
++		if (pos == nentries) {
++			// put back values of the time when this chain was called
++			e = cl_s[chain_nr].cs.e;
++			if (cl_s[chain_nr].from != -1)
++				nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++			else
++				nentries = chain->nentries;
++			pos = cl_s[chain_nr].cs.n;
++			// make sure we won't see a loop that isn't one
++			cl_s[chain_nr].cs.n = 0;
++			chain_nr = cl_s[chain_nr].from;
++			if (pos == nentries)
++				continue;
++		}
++		t = (struct ebt_entry_target *)
++		   (((char *)e) + e->target_offset);
++		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++			goto letscontinue;
++		if (e->target_offset + sizeof(struct ebt_standard_target) >
++		   e->next_offset) {
++			BUGPRINT("Standard target size too big\n");
++			return -1;
++		}
++		verdict = ((struct ebt_standard_target *)t)->verdict;
++		if (verdict >= 0) { // jump to another chain
++			struct ebt_entries *hlp2 =
++			   (struct ebt_entries *)(base + verdict);
++			for (i = 0; i < udc_cnt; i++)
++				if (hlp2 == cl_s[i].cs.chaininfo)
++					break;
++			// bad destination or loop
++			if (i == udc_cnt) {
++				BUGPRINT("bad destination\n");
++				return -1;
++			}
++			if (cl_s[i].cs.n) {
++				BUGPRINT("loop\n");
++				return -1;
++			}
++			cl_s[i].cs.n = pos + 1;
++			pos = 0;
++			cl_s[i].cs.e = ((void *)e + e->next_offset);
++			e = (struct ebt_entry *)(hlp2->data);
++			nentries = hlp2->nentries;
++			cl_s[i].from = chain_nr;
++			chain_nr = i;
++			// this udc is accessible from the base chain for hooknr
++			cl_s[i].hookmask |= (1 << hooknr);
++			continue;
++		}
++letscontinue:
++		e = (void *)e + e->next_offset;
++		pos++;
++	}
++	return 0;
++}
++
+ // do the parsing of the table/chains/entries/matches/watchers/targets, heh
+ static int translate_table(struct ebt_replace *repl,
+    struct ebt_table_info *newinfo)
+ {
+-	unsigned int i, j, k;
++	unsigned int i, j, k, udc_cnt;
+ 	int ret;
++	struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
+ 
+ 	i = 0;
+ 	while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+@@ -553,10 +724,8 @@
+ 		i = j;
+ 	}
+ 
+-	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ 		newinfo->hook_entry[i] = NULL;
+-		newinfo->counter_entry[i] = 0;
+-	}
+ 
+ 	newinfo->entries_size = repl->entries_size;
+ 	newinfo->nentries = repl->nentries;
+@@ -566,10 +735,11 @@
+ 	j = 0; // holds the up to now counted entries for the chain
+ 	k = 0; // holds the total nr. of entries, should equal
+ 	       // newinfo->nentries afterwards
++	udc_cnt = 0; // will hold the nr. of user defined chains (udc)
+ 	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ 	   ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+ 	   repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+-	   repl->valid_hooks);
++	   &udc_cnt, repl->valid_hooks);
+ 
+ 	if (ret != 0)
+ 		return ret;
+@@ -587,22 +757,70 @@
+ 	// check if all valid hooks have a chain
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if (newinfo->hook_entry[i] == NULL &&
+-		   (repl->valid_hooks & (1 << i))){
++		   (repl->valid_hooks & (1 << i))) {
+ 			BUGPRINT("Valid hook without chain\n");
+ 			return -EINVAL;
+ 		}
+ 	}
+ 
++	// Get the location of the udc, put them in an array
++	// While we're at it, allocate the chainstack
++	if (udc_cnt) {
++		// this will get free'd in do_replace()/ebt_register_table()
++		// if an error occurs
++		newinfo->chainstack = (struct ebt_chainstack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++		if (!newinfo->chainstack)
++			return -ENOMEM;
++		cl_s = (struct ebt_cl_stack *)
++		   vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++		if (!cl_s)
++			return -ENOMEM;
++		i = 0; // the i'th udc
++		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++		   ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++		   repl->valid_hooks, cl_s);
++		// sanity check
++		if (i != udc_cnt) {
++			BUGPRINT("i != udc_cnt\n");
++			vfree(cl_s);
++			return -EFAULT;
++		}
++	}
++
++	// Check for loops
++	for (i = 0; i < NF_BR_NUMHOOKS; i++)
++		if (repl->valid_hooks & (1 << i))
++			if (check_chainloops(newinfo->hook_entry[i],
++			   cl_s, udc_cnt, i, newinfo->entries)) {
++				if (cl_s)
++					vfree(cl_s);
++				return -EINVAL;
++			}
++
++	// we now know the following (along with E=mc²):
++	// - the nr of entries in each chain is right
++	// - the size of the allocated space is right
++	// - all valid hooks have a corresponding chain
++	// - there are no loops
++	// - wrong data can still be on the level of a single entry
++	// - could be there are jumps to places that are not the
++	//   beginning of a chain. This can only occur in chains that
++	//   are not accessible from any base chains, so we don't care.
++
+ 	// we just don't trust anything
+ 	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	// used to know what we need to clean up if something goes wrong
+ 	i = 0;
+ 	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+-	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++	   ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++	   cl_s, udc_cnt);
+ 	if (ret != 0) {
+ 		EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ 		   ebt_cleanup_entry, &i);
+ 	}
++	if (cl_s)
++		vfree(cl_s);
+ 	return ret;
+ }
+ 
+@@ -690,6 +908,8 @@
+ 	else
+ 		counterstmp = NULL;
+ 
++	// this can get initialized by translate_table()
++	newinfo->chainstack = NULL;
+ 	ret = translate_table(&tmp, newinfo);
+ 
+ 	if (ret != 0)
+@@ -739,6 +959,8 @@
+ 	vfree(table->entries);
+ 	if (table->counters)
+ 		vfree(table->counters);
++	if (table->chainstack)
++		vfree(table->chainstack);
+ 	vfree(table);
+ 
+ 	if (counterstmp)
+@@ -752,6 +974,9 @@
+ free_counterstmp:
+ 	if (counterstmp)
+ 		vfree(counterstmp);
++	// can be initialized in translate_table()
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
+ free_entries:
+ 	if (newinfo->entries)
+ 		vfree(newinfo->entries);
+@@ -877,6 +1102,7 @@
+ 		newinfo->counters = NULL;
+ 
+ 	// fill in newinfo and parse the entries
++	newinfo->chainstack = NULL;
+ 	ret = translate_table(table->table, newinfo);
+ 	if (ret != 0) {
+ 		BUGPRINT("Translate_table failed\n");
+@@ -909,6 +1135,8 @@
+ free_counters:
+ 	if (newinfo->counters)
+ 		vfree(newinfo->counters);
++	if (newinfo->chainstack)
++		vfree(newinfo->chainstack);
+ free_entries:
+ 	vfree(newinfo->entries);
+ free_newinfo:
+@@ -931,6 +1159,8 @@
+ 		vfree(table->private->counters);
+ 	if (table->private->entries)
+ 		vfree(table->private->entries);
++	if (table->private->chainstack)
++		vfree(table->private->chainstack);
+ 	vfree(table->private);
+ 	MOD_DEC_USE_COUNT;
+ }
+@@ -1091,8 +1321,6 @@
+ 		return -EFAULT;
+ 	}
+ 	// make userspace's life easier
+-	memcpy(tmp.counter_entry, info->counter_entry,
+-	   NF_BR_NUMHOOKS * sizeof(int));
+ 	memcpy(tmp.hook_entry, info->hook_entry,
+ 	   NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+--- linux/include/linux/netfilter_bridge/ebtables.h	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebtables.h	Thu Jun 27 20:18:07 2002
+@@ -17,6 +17,7 @@
+ #include <linux/if_ether.h> // ETH_ALEN
+ 
+ #define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ 
+ // [gs]etsockopt numbers
+@@ -30,18 +31,29 @@
+ #define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+ #define EBT_SO_GET_MAX          (EBT_SO_GET_ENTRIES+1)
+ 
+-#define EBT_ACCEPT   0
+-#define EBT_DROP     1
+-#define EBT_CONTINUE 2
+-#define NUM_STANDARD_TARGETS   3
++// verdicts >0 are "branches"
++#define EBT_ACCEPT   -1
++#define EBT_DROP     -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN   -4
++#define NUM_STANDARD_TARGETS   4
++
++struct ebt_counter
++{
++	__u64 pcnt;
++};
+ 
+ struct ebt_entries {
+ 	// this field is always set to zero (including userspace).
+ 	// See EBT_ENTRY_OR_ENTRIES.
+ 	// Must be same size as ebt_entry.bitmask
+ 	__u32 distinguisher;
+-	// one standard (accept or drop) per hook
+-	__u8 policy;
++	// the chain name
++	char name[EBT_CHAIN_MAXNAMELEN];
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// one standard (accept, drop, return) per hook
++	int policy;
+ 	// nr. of entries
+ 	__u32 nentries;
+ 	// entry list
+@@ -76,11 +88,6 @@
+ #define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+    | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+ 
+-struct ebt_counter
+-{
+-	__u64 pcnt;
+-};
+-
+ struct ebt_entry_match
+ {
+ 	union {
+@@ -118,7 +125,7 @@
+ struct ebt_standard_target
+ {
+ 	struct ebt_entry_target target;
+-	__u8 verdict;
++	int verdict;
+ };
+ 
+ // one entry
+@@ -158,8 +165,6 @@
+ 	unsigned int entries_size;
+ 	// start of the chains
+ 	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+-	// how many counters in front of it?
+-	unsigned int counter_entry[NF_BR_NUMHOOKS];
+ 	// nr of counters userspace expects back
+ 	unsigned int num_counters;
+ 	// where the kernel will put the old counters
+@@ -178,7 +183,7 @@
+ 	   const struct net_device *out, const void *matchdata,
+ 	   unsigned int datalen, const struct ebt_counter *c);
+ 	// 0 == let it in
+-	int (*check)(const char *tablename, unsigned int hooknr,
++	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+ 	void (*destroy)(void *matchdata, unsigned int datalen);
+ 	struct module *me;
+@@ -192,7 +197,7 @@
+ 	   const struct net_device *out, const void *watcherdata,
+ 	   unsigned int datalen, const struct ebt_counter *c);
+ 	// 0 == let it in
+-	int (*check)(const char *tablename, unsigned int hooknr,
++	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+ 	void (*destroy)(void *watcherdata, unsigned int datalen);
+ 	struct module *me;
+@@ -203,19 +208,27 @@
+ 	struct list_head list;
+ 	const char name[EBT_FUNCTION_MAXNAMELEN];
+ 	// returns one of the standard verdicts
+-	__u8 (*target)(struct sk_buff **pskb,
++	int (*target)(struct sk_buff **pskb,
+ 	       unsigned int hooknr,
+ 	       const struct net_device *in,
+ 	       const struct net_device *out,
+ 	       const void *targetdata,
+ 	       unsigned int datalen);
+ 	// 0 == let it in
+-	int (*check)(const char *tablename, unsigned int hooknr,
++	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+ 	void (*destroy)(void *targetdata, unsigned int datalen);
+ 	struct module *me;
+ };
+ 
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++	struct ebt_entries *chaininfo; // pointer to chain data
++	struct ebt_entry *e; // pointer to entry data
++	unsigned int n; // n'th entry
++};
++
+ struct ebt_table_info
+ {
+ 	// total size of the entries
+@@ -223,9 +236,9 @@
+ 	unsigned int nentries;
+ 	// pointers to the start of the chains
+ 	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+-	// how many counters in front of the counters bolonging to a chain
+-	unsigned int counter_entry[NF_BR_NUMHOOKS];
+ 	struct ebt_counter *counters;
++	// room to maintain the stack used for jumping from and into udc
++	struct ebt_chainstack *chainstack;
+ 	char *entries;
+ };
+ 
+--- linux/include/linux/netfilter_bridge/ebt_vlan.h	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_vlan.h	Thu Jun 27 19:11:50 2002
+@@ -3,14 +3,16 @@
+ 
+ #define EBT_VLAN_ID	0x01
+ #define EBT_VLAN_PRIO	0x02
+-#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO)
++#define EBT_VLAN_ENCAP	0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+ #define EBT_VLAN_MATCH "vlan"
+ 
+ struct ebt_vlan_info {
+ 	__u16 id;		/* VLAN ID {1-4095} */
+-	__u16 prio;		/* VLAN Priority {0-7} */
++	__u8 prio;		/* VLAN User Priority {0-7} */
++	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
+ 	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
+-				   bit 2=1 - Pirority arg */
++				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+ 	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+ 				   bit 2=1 - inversed Pirority arg */
+ };
+--- linux/include/linux/netfilter_bridge/ebt_nat.h	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_nat.h	Thu Jun 27 19:11:50 2002
+@@ -4,8 +4,8 @@
+ struct ebt_nat_info
+ {
+ 	unsigned char mac[ETH_ALEN];
+-	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+-	__u8 target;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
+ };
+ #define EBT_SNAT_TARGET "snat"
+ #define EBT_DNAT_TARGET "dnat"
+--- linux/include/linux/netfilter_bridge/ebt_redirect.h	Fri Jun 28 18:00:15 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_redirect.h	Thu Jun 27 19:11:50 2002
+@@ -3,8 +3,8 @@
+ 
+ struct ebt_redirect_info
+ {
+-	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+-	__u8 target;
++	// EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++	int target;
+ };
+ #define EBT_REDIRECT_TARGET "redirect"
+ 
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5-rc2.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5-rc2.001.diff
new file mode 100644
index 0000000..32c9556
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5-rc2.001.diff
@@ -0,0 +1,2814 @@
+--- linux-2.4.19-rc1/net/bridge/netfilter/Makefile	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/Makefile	Sat Aug 31 12:59:08 2002
+@@ -15,7 +15,6 @@
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+ obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+ obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+-obj-$(CONFIG_BRIDGE_DB) += br_db.o
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+--- linux-2.4.19-rc1/net/bridge/netfilter/Config.in	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/Config.in	Sat Aug 31 12:59:08 2002
+@@ -5,7 +5,7 @@
+ dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+@@ -14,5 +14,4 @@
+ dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
+-dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_filter.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_filter.c	Sat Aug 31 12:59:08 2002
+@@ -9,7 +9,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+ #include <linux/module.h>
+ 
+ #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+@@ -42,10 +41,9 @@
+   RW_LOCK_UNLOCKED, check, NULL
+ };
+ 
+-static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_filter);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_nat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_nat.c	Sat Aug 31 12:59:08 2002
+@@ -9,8 +9,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netdevice.h>
+ #include <linux/module.h>
+ #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+    (1 << NF_BR_POST_ROUTING))
+@@ -43,17 +41,15 @@
+ };
+ 
+ static unsigned int
+-ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *))
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+ 
+-static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_broute.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_broute.c	Sat Aug 31 12:59:08 2002
+@@ -12,8 +12,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netdevice.h>
+ #include <linux/module.h>
+ #include <linux/if_bridge.h>
+ #include <linux/brlock.h>
+@@ -43,10 +41,8 @@
+ };
+ 
+ static unsigned int
+-ebt_broute (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &broute_table);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_redirect.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_redirect.c	Sat Aug 31 12:59:08 2002
+@@ -10,8 +10,6 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_redirect.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+ #include <net/sock.h>
+ #include "../br_private.h"
+@@ -20,7 +18,7 @@
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+ 
+ 	if (hooknr != NF_BR_BROUTING)
+ 		memcpy((**pskb).mac.ethernet->h_dest,
+@@ -30,24 +28,23 @@
+ 		   in->dev_addr, ETH_ALEN);
+ 		(*pskb)->pkt_type = PACKET_HOST;
+ 	}
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	CLEAR_BASE_CHAIN_BIT;
+ 	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ 	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_redirect_info))
+-		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_arp.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_arp.c	Sat Aug 31 12:59:08 2002
+@@ -14,73 +14,68 @@
+ #include <linux/if_arp.h>
+ #include <linux/module.h>
+ 
+-#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
+-static int ebt_filter_arp(const struct sk_buff *skb,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *data,
+-	       unsigned int datalen, const struct ebt_counter *c)
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
+ {
+-	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+ 
+-	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+ 	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
+-		return 1;
+-	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+ 	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
+-		return 1;
+-	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+ 	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
+-		return 1;
++		return EBT_NOMATCH;
+ 
+-	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
+ 	{
+-		__u32 arp_len = sizeof(struct arphdr) +
+-		   (2*(((*skb).nh.arph)->ar_hln)) +
+-		   (2*(((*skb).nh.arph)->ar_pln));
+-		__u32 dst;
+-		__u32 src;
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
+ 
+- 		// Make sure the packet is long enough.
++		// Make sure the packet is long enough.
+ 		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+-			return 1;
+-		// IPV4 addresses are always 4 bytes.
+-		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
+-			return 1;
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
+ 
+-		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++		if (info->bitmask & EBT_ARP_SRC_IP) {
+ 			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
+-			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
+-			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
+ 			   EBT_ARP_SRC_IP))
+-				return 1;
++				return EBT_NOMATCH;
+ 		}
+ 
+-		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++		if (info->bitmask & EBT_ARP_DST_IP) {
+ 			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
+ 			   (2*(((*skb).nh.arph)->ar_hln)) +
+-			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
+-			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
+ 			   EBT_ARP_DST_IP))
+-				return 1;
++				return EBT_NOMATCH;
+ 		}
+ 	}
+-	return 0;
++	return EBT_MATCH;
+ }
+ 
+ static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+ 
+ 	if (datalen != sizeof(struct ebt_arp_info))
+ 		return -EINVAL;
+-	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
+-	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
+-	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
+ 	   e->invflags & EBT_IPROTO)
+ 		return -EINVAL;
+-	if (infostuff->bitmask & ~EBT_ARP_MASK)
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_ip.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_ip.c	Sat Aug 31 12:59:08 2002
+@@ -13,49 +13,41 @@
+ #include <linux/ip.h>
+ #include <linux/module.h>
+ 
+-#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
+-static int ebt_filter_ip(const struct sk_buff *skb,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *data,
+-	       unsigned int datalen, const struct ebt_counter *c)
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
+ {
+-	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+ 
+-	if (infostuff->bitmask & EBT_IP_TOS &&
+-	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
+-		return 1;
+-	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
+ 	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
+-		return 1;
+-	if (infostuff->bitmask & EBT_IP_SOURCE &&
+-	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
+-	   infostuff->saddr, EBT_IP_SOURCE))
+-		return 1;
+-	if ((infostuff->bitmask & EBT_IP_DEST) &&
+-	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
+-	   infostuff->daddr, EBT_IP_DEST))
+-		return 1;
+-	return 0;
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
+ }
+ 
+ static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+ 
+-	if (datalen != sizeof(struct ebt_ip_info)) {
++	if (datalen != sizeof(struct ebt_ip_info))
+ 		return -EINVAL;
+-	}
+-	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
+-	    e->ethproto != __constant_htons(ETH_P_IP) ||
+-	    e->invflags & EBT_IPROTO)
+-	{
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
+ 		return -EINVAL;
+-	}
+-	if (infostuff->bitmask & ~EBT_IP_MASK) {
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+ 		return -EINVAL;
+-	}
+ 	return 0;
+ }
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_vlan.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_vlan.c	Sat Aug 31 12:59:08 2002
+@@ -36,10 +36,10 @@
+ 
+ 
+ #define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
+-#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
+-#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
+-#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
+-#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
+ 
+ /*
+  * Function description: ebt_filter_vlan() is main engine for 
+@@ -63,9 +63,9 @@
+ 		 const struct net_device *in,
+ 		 const struct net_device *out,
+ 		 const void *data,
+-		 unsigned int datalen, const struct ebt_counter *c)
++		 unsigned int datalen)
+ {
+-	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
+ 	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
+ 
+ 	unsigned short TCI;	/* Whole TCI, given from parsed frame */
+@@ -109,7 +109,7 @@
+ 			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
+ 			DEBUG_MSG
+ 			    ("matched rule id=%s%d for frame id=%d\n",
+-			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
+ 		}
+ 	} else {
+ 		/*
+@@ -119,7 +119,7 @@
+ 			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
+ 			DEBUG_MSG
+ 			    ("matched rule prio=%s%d for frame prio=%d\n",
+-			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
+ 			     prio);
+ 		}
+ 	}
+@@ -130,7 +130,7 @@
+ 		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
+ 		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
+ 			   INV_FLAG (EBT_VLAN_ENCAP),
+-			   ntohs (infostuff->encap), ntohs (encap));
++			   ntohs (info->encap), ntohs (encap));
+ 	}
+ 	/*
+ 	 * All possible extension parameters was parsed.
+@@ -159,7 +159,7 @@
+ 		const struct ebt_entry *e, void *data,
+ 		unsigned int datalen)
+ {
+-	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+ 
+ 	/*
+ 	 * Parameters buffer overflow check 
+@@ -175,7 +175,7 @@
+ 	 * Is it 802.1Q frame checked?
+ 	 */
+ 	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
+-		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+ 			   (unsigned short) ntohs (e->ethproto));
+ 		return -EINVAL;
+ 	}
+@@ -184,18 +184,18 @@
+ 	 * Check for bitmask range 
+ 	 * True if even one bit is out of mask
+ 	 */
+-	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++	if (info->bitmask & ~EBT_VLAN_MASK) {
+ 		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
+-			   infostuff->bitmask, EBT_VLAN_MASK);
++			   info->bitmask, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+ 	/*
+ 	 * Check for inversion flags range 
+ 	 */
+-	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++	if (info->invflags & ~EBT_VLAN_MASK) {
+ 		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
+-			   infostuff->invflags, EBT_VLAN_MASK);
++			   info->invflags, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -223,11 +223,11 @@
+ 	 * For Linux, N = 4094.
+ 	 */
+ 	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
+-		if (!!infostuff->id) {	/* if id!=0 => check vid range */
+-			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
+ 				DEBUG_MSG
+ 				    ("vlan id %d is out of range (1-4094)\n",
+-				     infostuff->id);
++				     info->id);
+ 				return -EINVAL;
+ 			}
+ 			/*
+@@ -240,10 +240,10 @@
+ 			 * if id=0 (null VLAN ID)  => Check for user_priority range 
+ 			 */
+ 			if (GET_BITMASK (EBT_VLAN_PRIO)) {
+-				if ((unsigned char) infostuff->prio > 7) {
++				if ((unsigned char) info->prio > 7) {
+ 					DEBUG_MSG
+ 					    ("prio %d is out of range (0-7)\n",
+-					     infostuff->prio);
++					     info->prio);
+ 					return -EINVAL;
+ 				}
+ 			}
+@@ -254,7 +254,7 @@
+ 		}
+ 	} else {		/* VLAN Id not set */
+ 		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
+-			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
+ 			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
+ 		}
+ 	}
+@@ -266,10 +266,10 @@
+ 	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
+ 	 */
+ 	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
+-		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
+ 			DEBUG_MSG
+ 			    ("encap packet length %d is less than minimal %d\n",
+-			     ntohs (infostuff->encap), ETH_ZLEN);
++			     ntohs (info->encap), ETH_ZLEN);
+ 			return -EINVAL;
+ 		}
+ 	}
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_log.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_log.c	Sat Aug 31 12:59:08 2002
+@@ -20,67 +20,56 @@
+ static int ebt_log_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
+ 
+ 	if (datalen != sizeof(struct ebt_log_info))
+ 		return -EINVAL;
+-	if (loginfo->bitmask & ~EBT_LOG_MASK)
++	if (info->bitmask & ~EBT_LOG_MASK)
+ 		return -EINVAL;
+-	if (loginfo->loglevel >= 8)
++	if (info->loglevel >= 8)
+ 		return -EINVAL;
+-	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+ 	return 0;
+ }
+ 
+ static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const void *data, unsigned int datalen,
+-   const struct ebt_counter *c)
++   const struct net_device *out, const void *data, unsigned int datalen)
+ {
+-	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
+ 	char level_string[4] = "< >";
+-	level_string[1] = '0' + loginfo->loglevel;
++	level_string[1] = '0' + info->loglevel;
+ 
+ 	spin_lock_bh(&ebt_log_lock);
+ 	printk(level_string);
+-	// max length: 29 + 10 + 2 * 16
+-	printk("%s IN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
+ 
+ 	if (skb->dev->hard_header_len) {
+ 		int i;
+ 		unsigned char *p = (skb->mac.ethernet)->h_source;
++
+ 		printk("MAC source = ");
+ 		for (i = 0; i < ETH_ALEN; i++,p++)
+-			printk("%02x%c", *p,
+-			       i == ETH_ALEN - 1
+-			       ? ' ':':');// length: 31
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ 		printk("MAC dest = ");
+ 		p = (skb->mac.ethernet)->h_dest;
+ 		for (i = 0; i < ETH_ALEN; i++,p++)
+-			printk("%02x%c", *p,
+-			       i == ETH_ALEN - 1
+-			       ? ' ':':');// length: 29
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ 	}
+-	// length: 14
+ 	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
+ 
+-	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
+ 	   htons(ETH_P_IP)){
+ 		struct iphdr *iph = skb->nh.iph;
+-		// max length: 46
+ 		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+ 		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+-		// max length: 26
+ 		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
+ 	}
+ 
+-	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	if ((info->bitmask & EBT_LOG_ARP) &&
+ 	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
+ 	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
+ 		struct arphdr * arph = skb->nh.arph;
+-		// max length: 40
+ 		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+ 		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
+ 		   ntohs(arph->ar_op));
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_mark.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_mark.c	Sat Aug 31 12:59:08 2002
+@@ -1,5 +1,5 @@
+ /*
+- *  ebt_mark_t
++ *  ebt_mark
+  *
+  *	Authors:
+  *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+@@ -11,42 +11,35 @@
+ // The mark target can be used in any chain
+ // I believe adding a mangle table just for marking is total overkill
+ // Marking a frame doesn't really change anything in the frame anyway
+-// The target member of the struct ebt_vlan_info provides the same
+-// functionality as a separate table
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_mark_t.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+-#include <net/sock.h>
+-#include "../br_private.h"
+ 
+ static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+ 
+-	if ((*pskb)->nfmark != infostuff->mark) {
+-		(*pskb)->nfmark = infostuff->mark;
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
+ 		(*pskb)->nfcache |= NFC_ALTERED;
+ 	}
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
+-		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if (datalen != sizeof(struct ebt_mark_t_info))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_mark_m.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_mark_m.c	Sat Aug 31 12:59:08 2002
+@@ -14,7 +14,7 @@
+ 
+ static int ebt_filter_mark(const struct sk_buff *skb,
+    const struct net_device *in, const struct net_device *out, const void *data,
+-   unsigned int datalen, const struct ebt_counter *c)
++   unsigned int datalen)
+ {
+ 	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+ 
+@@ -28,15 +28,14 @@
+ {
+         struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+ 
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
+ 	if (info->bitmask & ~EBT_MARK_MASK)
+ 		return -EINVAL;
+ 	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+ 		return -EINVAL;
+ 	if (!info->bitmask)
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_mark_m_info)) {
+-		return -EINVAL;
+-	}
+ 	return 0;
+ }
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_snat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_snat.c	Sat Aug 31 12:59:08 2002
+@@ -10,38 +10,34 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_nat.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+-#include <net/sock.h>
+ 
+ static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+ 
+-	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+-	if (strcmp(tablename, "nat"))
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_nat_info))
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
+ 		return -EINVAL;
+ 	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_dnat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_dnat.c	Sat Aug 31 12:59:08 2002
+@@ -10,8 +10,6 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_nat.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+ #include <net/sock.h>
+ 
+@@ -19,29 +17,28 @@
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+ 
+-	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	CLEAR_BASE_CHAIN_BIT;
+ 	if ( (strcmp(tablename, "nat") ||
+ 	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ 	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtables.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtables.c	Sat Aug 31 12:59:08 2002
+@@ -22,10 +22,6 @@
+ #include <linux/kmod.h>
+ #include <linux/module.h>
+ #include <linux/vmalloc.h>
+-#include <linux/skbuff.h>
+-#include <linux/if_ether.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netfilter_ipv4.h>
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/spinlock.h>
+ #include <asm/uaccess.h>
+@@ -40,6 +36,21 @@
+ #include <linux/netfilter_ipv4/listhelp.h>
+ 
+ #if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
+ #define BUGPRINT(args) print_string(args);
+ #else
+ #define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+@@ -65,8 +76,6 @@
+ 
+ 
+ 
+-static void print_string(char *str);
+-
+ static DECLARE_MUTEX(ebt_mutex);
+ static LIST_HEAD(ebt_tables);
+ static LIST_HEAD(ebt_targets);
+@@ -78,20 +87,20 @@
+ 
+ static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+    const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const struct ebt_counter *c)
++   const struct net_device *out)
+ {
+ 	w->u.watcher->watcher(skb, in, out, w->data,
+-	   w->watcher_size, c);
++	   w->watcher_size);
+ 	// watchers don't give a verdict
+ 	return 0;
+ }
+ 
+ static inline int ebt_do_match (struct ebt_entry_match *m,
+    const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const struct ebt_counter *c)
++   const struct net_device *out)
+ {
+ 	return m->u.match->match(skb, in, out, m->data,
+-	   m->match_size, c);
++	   m->match_size);
+ }
+ 
+ static inline int ebt_dev_check(char *entry, const struct net_device *device)
+@@ -100,48 +109,48 @@
+ 		return 0;
+ 	if (!device)
+ 		return 1;
+-	return !!strncmp(entry, device->name, IFNAMSIZ);
++	return !!strcmp(entry, device->name);
+ }
+ 
+-#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+ // process standard matches
+-static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+    const struct net_device *in, const struct net_device *out)
+ {
+ 	int verdict, i;
+ 
+-	if (p->bitmask & EBT_802_3) {
+-		if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+ 			return 1;
+-	} else if (!(p->bitmask & EBT_NOPROTO) &&
+-	   FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+ 		return 1;
+ 
+-	if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+ 		return 1;
+-	if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+ 		return 1;
+-	if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
+-	   p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+ 		return 1;
+-	if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
+-	   (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+ 		return 1;
+-	
+-	if (p->bitmask & EBT_SOURCEMAC) {
++
++	if (e->bitmask & EBT_SOURCEMAC) {
+ 		verdict = 0;
+ 		for (i = 0; i < 6; i++)
+-			verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
+-			   p->sourcemsk[i];
+-		if (FWINV(verdict != 0, EBT_ISOURCE) )
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ 			return 1;
+ 	}
+-	if (p->bitmask & EBT_DESTMAC) {
++	if (e->bitmask & EBT_DESTMAC) {
+ 		verdict = 0;
+ 		for (i = 0; i < 6; i++)
+-			verdict |= (h->h_dest[i] ^ p->destmac[i]) &
+-			   p->destmsk[i];
+-		if (FWINV(verdict != 0, EBT_IDEST) )
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
+ 			return 1;
+ 	}
+ 	return 0;
+@@ -163,7 +172,7 @@
+ 	struct ebt_table_info *private = table->private;
+ 
+ 	read_lock_bh(&table->lock);
+-	cb_base = COUNTER_BASE(private->counters, private->nentries, \
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
+ 	   cpu_number_map(smp_processor_id()));
+ 	if (private->chainstack)
+ 		cs = private->chainstack[cpu_number_map(smp_processor_id())];
+@@ -180,8 +189,7 @@
+ 		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
+ 			goto letscontinue;
+ 
+-		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
+-		   out, counter_base + i) != 0)
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+ 			goto letscontinue;
+ 
+ 		// increase counter
+@@ -190,7 +198,7 @@
+ 		// these should only watch: not modify, nor tell us
+ 		// what to do with the packet
+ 		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+-		   out, counter_base + i);
++		   out);
+ 
+ 		t = (struct ebt_entry_target *)
+ 		   (((char *)point) + point->target_offset);
+@@ -210,11 +218,13 @@
+ 		}
+ 		if (verdict == EBT_RETURN) {
+ letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
+ 			if (sp == 0) {
+ 				BUGPRINT("RETURN on base chain");
+ 				// act like this is EBT_CONTINUE
+ 				goto letscontinue;
+ 			}
++#endif
+ 			sp--;
+ 			// put all the local variables right
+ 			i = cs[sp].n;
+@@ -227,11 +237,13 @@
+ 		}
+ 		if (verdict == EBT_CONTINUE)
+ 			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
+ 		if (verdict < 0) {
+ 			BUGPRINT("bogus standard verdict\n");
+ 			read_unlock_bh(&table->lock);
+ 			return NF_DROP;
+ 		}
++#endif
+ 		// jump to a udc
+ 		cs[sp].n = i + 1;
+ 		cs[sp].chaininfo = chaininfo;
+@@ -239,11 +251,13 @@
+ 		   (((char *)point) + point->next_offset);
+ 		i = 0;
+ 		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
+ 		if (chaininfo->distinguisher) {
+ 			BUGPRINT("jump to non-chain\n");
+ 			read_unlock_bh(&table->lock);
+ 			return NF_DROP;
+ 		}
++#endif
+ 		nentries = chaininfo->nentries;
+ 		point = (struct ebt_entry *)chaininfo->data;
+ 		counter_base = cb_base + chaininfo->counter_offset;
+@@ -266,12 +280,10 @@
+ 	return NF_DROP;
+ }
+ 
+-/* If it succeeds, returns element and locks mutex */
++// If it succeeds, returns element and locks mutex
+ static inline void *
+-find_inlist_lock_noload(struct list_head *head,
+-			const char *name,
+-			int *error,
+-			struct semaphore *mutex)
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
+ {
+ 	void *ret;
+ 
+@@ -291,11 +303,8 @@
+ #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+ #else
+ static void *
+-find_inlist_lock(struct list_head *head,
+-		 const char *name,
+-		 const char *prefix,
+-		 int *error,
+-		 struct semaphore *mutex)
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
+ {
+ 	void *ret;
+ 
+@@ -345,7 +354,6 @@
+ 	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+ 	   ((char *)e) + e->watchers_offset)
+ 		return -EINVAL;
+-	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+ 	if (!match)
+ 		return ret;
+@@ -374,7 +382,6 @@
+ 	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+ 	   ((char *)e) + e->target_offset)
+ 		return -EINVAL;
+-	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+ 	if (!watcher)
+ 		return ret;
+@@ -457,7 +464,7 @@
+ 	// a plain old entry, heh
+ 	if (sizeof(struct ebt_entry) > e->watchers_offset ||
+ 	   e->watchers_offset > e->target_offset ||
+-	   e->target_offset > e->next_offset) {
++	   e->target_offset >= e->next_offset) {
+ 		BUGPRINT("entry offsets not in right order\n");
+ 		return -EINVAL;
+ 	}
+@@ -537,6 +544,27 @@
+ }
+ 
+ static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
+ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+    const char *name, unsigned int *cnt, unsigned int valid_hooks,
+    struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+@@ -562,10 +590,6 @@
+ 		BUGPRINT("NOPROTO & 802_3 not allowed\n");
+ 		return -EINVAL;
+ 	}
+-	e->in[IFNAMSIZ - 1] = '\0';
+-	e->out[IFNAMSIZ - 1] = '\0';
+-	e->logical_in[IFNAMSIZ - 1] = '\0';
+-	e->logical_out[IFNAMSIZ - 1] = '\0';
+ 	// what hook do we belong to?
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if ((valid_hooks & (1 << i)) == 0)
+@@ -597,7 +621,6 @@
+ 	if (ret != 0)
+ 		goto cleanup_watchers;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+ 	if (!target)
+ 		goto cleanup_watchers;
+@@ -637,27 +660,6 @@
+ 	return ret;
+ }
+ 
+-static inline int
+-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+-{
+-	struct ebt_entry_target *t;
+-
+-	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+-		return 0;
+-	// we're done
+-	if (cnt && (*cnt)-- == 0)
+-		return 1;
+-	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+-	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+-	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-	if (t->u.target->destroy)
+-		t->u.target->destroy(t->data, t->target_size);
+-	if (t->u.target->me)
+-		__MOD_DEC_USE_COUNT(t->u.target->me);
+-
+-	return 0;
+-}
+-
+ // checks for loops and sets the hook mask for udc
+ // the hook mask for udc tells us from which base chains the udc can be
+ // accessed. This mask is a parameter to the check() functions of the extensions
+@@ -687,7 +689,6 @@
+ 		}
+ 		t = (struct ebt_entry_target *)
+ 		   (((char *)e) + e->target_offset);
+-		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+ 			goto letscontinue;
+ 		if (e->target_offset + sizeof(struct ebt_standard_target) >
+@@ -857,7 +858,6 @@
+ 	//   beginning of a chain. This can only occur in chains that
+ 	//   are not accessible from any base chains, so we don't care.
+ 
+-	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	// used to know what we need to clean up if something goes wrong
+ 	i = 0;
+ 	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+@@ -961,7 +961,7 @@
+ 	// the table doesn't like it
+ 	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+ 		goto free_unlock;
+-		
++
+ 	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+ 		BUGPRINT("Wrong nr. of counters requested\n");
+ 		ret = -EINVAL;
+@@ -979,8 +979,8 @@
+ 	t->private = newinfo;
+ 	write_unlock_bh(&t->lock);
+ 	up(&ebt_mutex);
+-	// So, a user can change the chains while having messed up his counter
+-	// allocation. Only reason why I do this is because this way the lock
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
+ 	// is held only once, while this doesn't bring the kernel into a
+ 	// dangerous state.
+ 	if (tmp.num_counters &&
+@@ -1220,11 +1220,10 @@
+ 
+ 	if ( !(tmp = (struct ebt_counter *)
+ 	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+-		MEMPRINT("Updata_counters && nomemory\n");
++		MEMPRINT("Update_counters && nomemory\n");
+ 		return -ENOMEM;
+ 	}
+ 
+-	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ 	if (!t)
+ 		goto free_tmp;
+@@ -1279,12 +1278,13 @@
+ static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+ {
+ 	int ret;
+-	char *hlp = ubase - base + (char *)e + e->target_offset;
++	char *hlp;
+ 	struct ebt_entry_target *t;
+ 
+ 	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ 		return 0;
+ 
++	hlp = ubase - base + (char *)e + e->target_offset;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ 	
+ 	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+@@ -1369,10 +1369,6 @@
+ 		BUGPRINT("Couldn't copy entries to userspace\n");
+ 		return -EFAULT;
+ 	}
+-	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
+-		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
+-		return -EFAULT;
+-	}
+ 	// set the match/watcher/target names right
+ 	return EBT_ENTRY_ITERATE(entries, entries_size,
+ 	   ebt_make_names, entries, tmp.entries);
+@@ -1454,21 +1450,6 @@
+     EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+ };
+ 
+-// Copyright (C) 1998 by Ori Pomerantz
+-// Print the string to the appropriate tty, the one
+-// the current task uses
+-static void print_string(char *str)
+-{
+-	struct tty_struct *my_tty;
+-
+-	/* The tty for the current task */
+-	my_tty = current->tty;
+-	if (my_tty != NULL) {
+-		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
+-		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
+-	}
+-}
+-
+ static int __init init(void)
+ {
+ 	int ret;
+@@ -1479,14 +1460,14 @@
+ 	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+ 		return ret;
+ 
+-	print_string("Ebtables v2.0 registered");
++	printk("Ebtables v2.0 registered");
+ 	return 0;
+ }
+ 
+ static void __exit fini(void)
+ {
+ 	nf_unregister_sockopt(&ebt_sockopts);
+-	print_string("Ebtables v2.0 unregistered");
++	printk("Ebtables v2.0 unregistered");
+ }
+ 
+ EXPORT_SYMBOL(ebt_register_table);
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebtables.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebtables.h	Sat Aug 31 12:59:08 2002
+@@ -40,9 +40,13 @@
+ #define EBT_RETURN   -4
+ #define NUM_STANDARD_TARGETS   4
+ 
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
+ struct ebt_counter
+ {
+-	__u64 pcnt;
++	uint64_t pcnt;
+ };
+ 
+ struct ebt_entries {
+@@ -135,7 +139,7 @@
+ 	// this needs to be the first field
+ 	unsigned int bitmask;
+ 	unsigned int invflags;
+-	__u16 ethproto;
++	uint16_t ethproto;
+ 	// the physical in-dev
+ 	char in[IFNAMSIZ];
+ 	// the logical in-dev
+@@ -183,7 +187,7 @@
+ 	// 0 == it matches
+ 	int (*match)(const struct sk_buff *skb, const struct net_device *in,
+ 	   const struct net_device *out, const void *matchdata,
+-	   unsigned int datalen, const struct ebt_counter *c);
++	   unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+@@ -197,7 +201,7 @@
+ 	const char name[EBT_FUNCTION_MAXNAMELEN];
+ 	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
+ 	   const struct net_device *out, const void *watcherdata,
+-	   unsigned int datalen, const struct ebt_counter *c);
++	   unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+@@ -210,12 +214,9 @@
+ 	struct list_head list;
+ 	const char name[EBT_FUNCTION_MAXNAMELEN];
+ 	// returns one of the standard verdicts
+-	int (*target)(struct sk_buff **pskb,
+-	       unsigned int hooknr,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *targetdata,
+-	       unsigned int datalen);
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+@@ -271,6 +272,16 @@
+    const struct net_device *in, const struct net_device *out,
+    struct ebt_table *table);
+ 
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
+ #endif /* __KERNEL__ */
+ 
+ // blatently stolen from ip_tables.h
+@@ -333,9 +344,9 @@
+ 		if (__ret != 0)                             \
+ 			break;                              \
+ 		if (__entry->bitmask != 0)                  \
+-		 __i += __entry->next_offset;               \
++			__i += __entry->next_offset;        \
+ 		else                                        \
+-		 __i += sizeof(struct ebt_entries);         \
++			__i += sizeof(struct ebt_entries);  \
+ 	}                                                   \
+ 	if (__ret == 0) {                                   \
+ 		if (__i != (size))                          \
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_arp.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_arp.h	Sat Aug 31 12:59:08 2002
+@@ -12,15 +12,15 @@
+ 
+ struct ebt_arp_info
+ {
+-	__u16 htype;
+-	__u16 ptype;
+-	__u16 opcode;
+-	__u32 saddr;
+-	__u32 smsk;
+-	__u32 daddr;
+-	__u32 dmsk;
+-	__u8  bitmask;
+-	__u8  invflags;
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_ip.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_ip.h	Sat Aug 31 12:59:08 2002
+@@ -11,14 +11,14 @@
+ // the same values are used for the invflags
+ struct ebt_ip_info
+ {
+-	__u32 saddr;
+-	__u32 daddr;
+-	__u32 smsk;
+-	__u32 dmsk;
+-	__u8  tos;
+-	__u8  protocol;
+-	__u8  bitmask;
+-	__u8  invflags;
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_vlan.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_vlan.h	Sat Aug 31 12:59:08 2002
+@@ -8,12 +8,12 @@
+ #define EBT_VLAN_MATCH "vlan"
+ 
+ struct ebt_vlan_info {
+-	__u16 id;		/* VLAN ID {1-4095} */
+-	__u8 prio;		/* VLAN User Priority {0-7} */
+-	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
+-	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
+ 				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+-	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+ 				   bit 2=1 - inversed Pirority arg */
+ };
+ 
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_log.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_log.h	Sat Aug 31 12:59:08 2002
+@@ -9,9 +9,9 @@
+ 
+ struct ebt_log_info
+ {
+-	__u8 loglevel;
+-	__u8 prefix[EBT_LOG_PREFIX_SIZE];
+-	__u32 bitmask;
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_mark_m.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_mark_m.h	Sat Aug 31 12:59:08 2002
+@@ -7,8 +7,8 @@
+ struct ebt_mark_m_info
+ {
+ 	unsigned long mark, mask;
+-	__u8 invert;
+-	__u8 bitmask;
++	uint8_t invert;
++	uint8_t bitmask;
+ };
+ #define EBT_MARK_MATCH "mark_m"
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/Makefile	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/Makefile	Sat Aug 31 12:59:08 2002
+@@ -15,7 +15,6 @@
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+ obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+ obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+-obj-$(CONFIG_BRIDGE_DB) += br_db.o
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+--- linux-2.4.19-rc1/net/bridge/netfilter/Config.in	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/Config.in	Sat Aug 31 12:59:08 2002
+@@ -5,7 +5,7 @@
+ dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+@@ -14,5 +14,4 @@
+ dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+ dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
+-dep_tristate '  Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_filter.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_filter.c	Sat Aug 31 12:59:08 2002
+@@ -9,7 +9,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+ #include <linux/module.h>
+ 
+ #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+@@ -42,10 +41,9 @@
+   RW_LOCK_UNLOCKED, check, NULL
+ };
+ 
+-static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++static unsigned int
++ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_filter);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_nat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_nat.c	Sat Aug 31 12:59:08 2002
+@@ -9,8 +9,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netdevice.h>
+ #include <linux/module.h>
+ #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+    (1 << NF_BR_POST_ROUTING))
+@@ -43,17 +41,15 @@
+ };
+ 
+ static unsigned int
+-ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *))
++ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+ 
+-static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++static unsigned int
++ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device *in
++   , const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtable_broute.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtable_broute.c	Sat Aug 31 12:59:08 2002
+@@ -12,8 +12,6 @@
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netdevice.h>
+ #include <linux/module.h>
+ #include <linux/if_bridge.h>
+ #include <linux/brlock.h>
+@@ -43,10 +41,8 @@
+ };
+ 
+ static unsigned int
+-ebt_broute (unsigned int hook, struct sk_buff **pskb,
+-			const struct net_device *in,
+-			const struct net_device *out,
+-			int (*okfn)(struct sk_buff *))
++ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
++   const struct net_device *out, int (*okfn)(struct sk_buff *))
+ {
+ 	return ebt_do_table(hook, pskb, in, out, &broute_table);
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_redirect.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_redirect.c	Sat Aug 31 12:59:08 2002
+@@ -10,8 +10,6 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_redirect.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+ #include <net/sock.h>
+ #include "../br_private.h"
+@@ -20,7 +18,7 @@
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+ 
+ 	if (hooknr != NF_BR_BROUTING)
+ 		memcpy((**pskb).mac.ethernet->h_dest,
+@@ -30,24 +28,23 @@
+ 		   in->dev_addr, ETH_ALEN);
+ 		(*pskb)->pkt_type = PACKET_HOST;
+ 	}
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++	struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (datalen != sizeof(struct ebt_redirect_info))
++		return -EINVAL;
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	CLEAR_BASE_CHAIN_BIT;
+ 	if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ 	     (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_redirect_info))
+-		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_arp.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_arp.c	Sat Aug 31 12:59:08 2002
+@@ -14,73 +14,68 @@
+ #include <linux/if_arp.h>
+ #include <linux/module.h>
+ 
+-#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
+-static int ebt_filter_arp(const struct sk_buff *skb,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *data,
+-	       unsigned int datalen, const struct ebt_counter *c)
++static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data, unsigned int datalen)
+ {
+-	struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+ 
+-	if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++	if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+ 	   ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
+-		return 1;
+-	if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+ 	   ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
+-		return 1;
+-	if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+ 	   ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
+-		return 1;
++		return EBT_NOMATCH;
+ 
+-	if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++	if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
+ 	{
+-		__u32 arp_len = sizeof(struct arphdr) +
+-		   (2*(((*skb).nh.arph)->ar_hln)) +
+-		   (2*(((*skb).nh.arph)->ar_pln));
+-		__u32 dst;
+-		__u32 src;
++		uint32_t arp_len = sizeof(struct arphdr) +
++		   (2 * (((*skb).nh.arph)->ar_hln)) +
++		   (2 * (((*skb).nh.arph)->ar_pln));
++		uint32_t dst;
++		uint32_t src;
+ 
+- 		// Make sure the packet is long enough.
++		// Make sure the packet is long enough.
+ 		if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+-			return 1;
+-		// IPV4 addresses are always 4 bytes.
+-		if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
+-			return 1;
++			return EBT_NOMATCH;
++		// IPv4 addresses are always 4 bytes.
++		if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
++			return EBT_NOMATCH;
+ 
+-		if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++		if (info->bitmask & EBT_ARP_SRC_IP) {
+ 			memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
+-			   ((*skb).nh.arph)->ar_hln, sizeof(__u32));
+-			if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++			   ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
++			if (FWINV(info->saddr != (src & info->smsk),
+ 			   EBT_ARP_SRC_IP))
+-				return 1;
++				return EBT_NOMATCH;
+ 		}
+ 
+-		if (infostuff->bitmask & EBT_ARP_DST_IP) {
++		if (info->bitmask & EBT_ARP_DST_IP) {
+ 			memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
+ 			   (2*(((*skb).nh.arph)->ar_hln)) +
+-			   (((*skb).nh.arph)->ar_pln), sizeof(__u32));
+-			if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++			   (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
++			if (FWINV(info->daddr != (dst & info->dmsk),
+ 			   EBT_ARP_DST_IP))
+-				return 1;
++				return EBT_NOMATCH;
+ 		}
+ 	}
+-	return 0;
++	return EBT_MATCH;
+ }
+ 
+ static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++	struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+ 
+ 	if (datalen != sizeof(struct ebt_arp_info))
+ 		return -EINVAL;
+-	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
+-	   (e->ethproto != __constant_htons(ETH_P_ARP) && 
+-	    e->ethproto != __constant_htons(ETH_P_RARP)) ||
++	if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
++	   e->ethproto != __constant_htons(ETH_P_RARP)) ||
+ 	   e->invflags & EBT_IPROTO)
+ 		return -EINVAL;
+-	if (infostuff->bitmask & ~EBT_ARP_MASK)
++	if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_ip.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_ip.c	Sat Aug 31 12:59:08 2002
+@@ -13,49 +13,41 @@
+ #include <linux/ip.h>
+ #include <linux/module.h>
+ 
+-#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
+-static int ebt_filter_ip(const struct sk_buff *skb,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *data,
+-	       unsigned int datalen, const struct ebt_counter *c)
++static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
++   const struct net_device *out, const void *data,
++   unsigned int datalen)
+ {
+-	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+ 
+-	if (infostuff->bitmask & EBT_IP_TOS &&
+-	   FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
+-		return 1;
+-	if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++	if (info->bitmask & EBT_IP_TOS &&
++	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
+ 	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
+-		return 1;
+-	if (infostuff->bitmask & EBT_IP_SOURCE &&
+-	   FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
+-	   infostuff->saddr, EBT_IP_SOURCE))
+-		return 1;
+-	if ((infostuff->bitmask & EBT_IP_DEST) &&
+-	   FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
+-	   infostuff->daddr, EBT_IP_DEST))
+-		return 1;
+-	return 0;
++		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_SOURCE &&
++	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
++	   info->saddr, EBT_IP_SOURCE))
++		return EBT_NOMATCH;
++	if ((info->bitmask & EBT_IP_DEST) &&
++	   FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
++	   info->daddr, EBT_IP_DEST))
++		return EBT_NOMATCH;
++	return EBT_MATCH;
+ }
+ 
+ static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++	struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+ 
+-	if (datalen != sizeof(struct ebt_ip_info)) {
++	if (datalen != sizeof(struct ebt_ip_info))
+ 		return -EINVAL;
+-	}
+-	if (e->bitmask & (EBT_NOPROTO | EBT_802_3) || 
+-	    e->ethproto != __constant_htons(ETH_P_IP) ||
+-	    e->invflags & EBT_IPROTO)
+-	{
++	if (e->ethproto != __constant_htons(ETH_P_IP) ||
++	   e->invflags & EBT_IPROTO)
+ 		return -EINVAL;
+-	}
+-	if (infostuff->bitmask & ~EBT_IP_MASK) {
++	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+ 		return -EINVAL;
+-	}
+ 	return 0;
+ }
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_vlan.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_vlan.c	Sat Aug 31 12:59:08 2002
+@@ -36,10 +36,10 @@
+ 
+ 
+ #define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
+-#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
+-#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
+-#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
+-#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return 1;
+ 
+ /*
+  * Function description: ebt_filter_vlan() is main engine for 
+@@ -63,9 +63,9 @@
+ 		 const struct net_device *in,
+ 		 const struct net_device *out,
+ 		 const void *data,
+-		 unsigned int datalen, const struct ebt_counter *c)
++		 unsigned int datalen)
+ {
+-	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;	/* userspace data */
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
+ 	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
+ 
+ 	unsigned short TCI;	/* Whole TCI, given from parsed frame */
+@@ -109,7 +109,7 @@
+ 			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
+ 			DEBUG_MSG
+ 			    ("matched rule id=%s%d for frame id=%d\n",
+-			     INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++			     INV_FLAG (EBT_VLAN_ID), info->id, id);
+ 		}
+ 	} else {
+ 		/*
+@@ -119,7 +119,7 @@
+ 			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
+ 			DEBUG_MSG
+ 			    ("matched rule prio=%s%d for frame prio=%d\n",
+-			     INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
+ 			     prio);
+ 		}
+ 	}
+@@ -130,7 +130,7 @@
+ 		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
+ 		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
+ 			   INV_FLAG (EBT_VLAN_ENCAP),
+-			   ntohs (infostuff->encap), ntohs (encap));
++			   ntohs (info->encap), ntohs (encap));
+ 	}
+ 	/*
+ 	 * All possible extension parameters was parsed.
+@@ -159,7 +159,7 @@
+ 		const struct ebt_entry *e, void *data,
+ 		unsigned int datalen)
+ {
+-	struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+ 
+ 	/*
+ 	 * Parameters buffer overflow check 
+@@ -175,7 +175,7 @@
+ 	 * Is it 802.1Q frame checked?
+ 	 */
+ 	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
+-		DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+ 			   (unsigned short) ntohs (e->ethproto));
+ 		return -EINVAL;
+ 	}
+@@ -184,18 +184,18 @@
+ 	 * Check for bitmask range 
+ 	 * True if even one bit is out of mask
+ 	 */
+-	if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++	if (info->bitmask & ~EBT_VLAN_MASK) {
+ 		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
+-			   infostuff->bitmask, EBT_VLAN_MASK);
++			   info->bitmask, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+ 	/*
+ 	 * Check for inversion flags range 
+ 	 */
+-	if (infostuff->invflags & ~EBT_VLAN_MASK) {
++	if (info->invflags & ~EBT_VLAN_MASK) {
+ 		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
+-			   infostuff->invflags, EBT_VLAN_MASK);
++			   info->invflags, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -223,11 +223,11 @@
+ 	 * For Linux, N = 4094.
+ 	 */
+ 	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
+-		if (!!infostuff->id) {	/* if id!=0 => check vid range */
+-			if (infostuff->id > 4094) {	/* check if id > than (0x0FFE) */
++		if (!!info->id) {	/* if id!=0 => check vid range */
++			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
+ 				DEBUG_MSG
+ 				    ("vlan id %d is out of range (1-4094)\n",
+-				     infostuff->id);
++				     info->id);
+ 				return -EINVAL;
+ 			}
+ 			/*
+@@ -240,10 +240,10 @@
+ 			 * if id=0 (null VLAN ID)  => Check for user_priority range 
+ 			 */
+ 			if (GET_BITMASK (EBT_VLAN_PRIO)) {
+-				if ((unsigned char) infostuff->prio > 7) {
++				if ((unsigned char) info->prio > 7) {
+ 					DEBUG_MSG
+ 					    ("prio %d is out of range (0-7)\n",
+-					     infostuff->prio);
++					     info->prio);
+ 					return -EINVAL;
+ 				}
+ 			}
+@@ -254,7 +254,7 @@
+ 		}
+ 	} else {		/* VLAN Id not set */
+ 		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
+-			infostuff->id = 0;	/* Set null VID (case for Priority-tagged frames) */
++			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
+ 			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
+ 		}
+ 	}
+@@ -266,10 +266,10 @@
+ 	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
+ 	 */
+ 	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
+-		if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
+ 			DEBUG_MSG
+ 			    ("encap packet length %d is less than minimal %d\n",
+-			     ntohs (infostuff->encap), ETH_ZLEN);
++			     ntohs (info->encap), ETH_ZLEN);
+ 			return -EINVAL;
+ 		}
+ 	}
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_log.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_log.c	Sat Aug 31 12:59:08 2002
+@@ -20,67 +20,56 @@
+ static int ebt_log_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
+ 
+ 	if (datalen != sizeof(struct ebt_log_info))
+ 		return -EINVAL;
+-	if (loginfo->bitmask & ~EBT_LOG_MASK)
++	if (info->bitmask & ~EBT_LOG_MASK)
+ 		return -EINVAL;
+-	if (loginfo->loglevel >= 8)
++	if (info->loglevel >= 8)
+ 		return -EINVAL;
+-	loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++	info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+ 	return 0;
+ }
+ 
+ static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const void *data, unsigned int datalen,
+-   const struct ebt_counter *c)
++   const struct net_device *out, const void *data, unsigned int datalen)
+ {
+-	struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++	struct ebt_log_info *info = (struct ebt_log_info *)data;
+ 	char level_string[4] = "< >";
+-	level_string[1] = '0' + loginfo->loglevel;
++	level_string[1] = '0' + info->loglevel;
+ 
+ 	spin_lock_bh(&ebt_log_lock);
+ 	printk(level_string);
+-	// max length: 29 + 10 + 2 * 16
+-	printk("%s IN=%s OUT=%s ",
+-	       loginfo->prefix,
+-	       in ? in->name : "",
+-	       out ? out->name : "");
++	printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
++	   out ? out->name : "");
+ 
+ 	if (skb->dev->hard_header_len) {
+ 		int i;
+ 		unsigned char *p = (skb->mac.ethernet)->h_source;
++
+ 		printk("MAC source = ");
+ 		for (i = 0; i < ETH_ALEN; i++,p++)
+-			printk("%02x%c", *p,
+-			       i == ETH_ALEN - 1
+-			       ? ' ':':');// length: 31
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ 		printk("MAC dest = ");
+ 		p = (skb->mac.ethernet)->h_dest;
+ 		for (i = 0; i < ETH_ALEN; i++,p++)
+-			printk("%02x%c", *p,
+-			       i == ETH_ALEN - 1
+-			       ? ' ':':');// length: 29
++			printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ 	}
+-	// length: 14
+ 	printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
+ 
+-	if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++	if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
+ 	   htons(ETH_P_IP)){
+ 		struct iphdr *iph = skb->nh.iph;
+-		// max length: 46
+ 		printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+ 		   NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+-		// max length: 26
+ 		printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
+ 	}
+ 
+-	if ((loginfo->bitmask & EBT_LOG_ARP) &&
++	if ((info->bitmask & EBT_LOG_ARP) &&
+ 	    ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
+ 	    (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
+ 		struct arphdr * arph = skb->nh.arph;
+-		// max length: 40
+ 		printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+ 		   ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
+ 		   ntohs(arph->ar_op));
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_mark.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_mark.c	Sat Aug 31 12:59:08 2002
+@@ -1,5 +1,5 @@
+ /*
+- *  ebt_mark_t
++ *  ebt_mark
+  *
+  *	Authors:
+  *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+@@ -11,42 +11,35 @@
+ // The mark target can be used in any chain
+ // I believe adding a mangle table just for marking is total overkill
+ // Marking a frame doesn't really change anything in the frame anyway
+-// The target member of the struct ebt_vlan_info provides the same
+-// functionality as a separate table
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_mark_t.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+-#include <net/sock.h>
+-#include "../br_private.h"
+ 
+ static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+ 
+-	if ((*pskb)->nfmark != infostuff->mark) {
+-		(*pskb)->nfmark = infostuff->mark;
++	if ((*pskb)->nfmark != info->mark) {
++		(*pskb)->nfmark = info->mark;
+ 		(*pskb)->nfcache |= NFC_ALTERED;
+ 	}
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++	struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
+-		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if (datalen != sizeof(struct ebt_mark_t_info))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (BASE_CHAIN && info->target == EBT_RETURN)
++		return -EINVAL;
++	CLEAR_BASE_CHAIN_BIT;
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_mark_m.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_mark_m.c	Sat Aug 31 12:59:08 2002
+@@ -14,7 +14,7 @@
+ 
+ static int ebt_filter_mark(const struct sk_buff *skb,
+    const struct net_device *in, const struct net_device *out, const void *data,
+-   unsigned int datalen, const struct ebt_counter *c)
++   unsigned int datalen)
+ {
+ 	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+ 
+@@ -28,15 +28,14 @@
+ {
+         struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+ 
++	if (datalen != sizeof(struct ebt_mark_m_info))
++		return -EINVAL;
+ 	if (info->bitmask & ~EBT_MARK_MASK)
+ 		return -EINVAL;
+ 	if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+ 		return -EINVAL;
+ 	if (!info->bitmask)
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_mark_m_info)) {
+-		return -EINVAL;
+-	}
+ 	return 0;
+ }
+ 
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_snat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_snat.c	Sat Aug 31 12:59:08 2002
+@@ -10,38 +10,34 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_nat.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+-#include <net/sock.h>
+ 
+ static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+ 
+-	memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++	memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
+-	if (strcmp(tablename, "nat"))
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	if (datalen != sizeof(struct ebt_nat_info))
++	CLEAR_BASE_CHAIN_BIT;
++	if (strcmp(tablename, "nat"))
+ 		return -EINVAL;
+ 	if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebt_dnat.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebt_dnat.c	Sat Aug 31 12:59:08 2002
+@@ -10,8 +10,6 @@
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_nat.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/skbuff.h>
+ #include <linux/module.h>
+ #include <net/sock.h>
+ 
+@@ -19,29 +17,28 @@
+    const struct net_device *in, const struct net_device *out,
+    const void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+ 
+-	memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++	memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
+ 	   ETH_ALEN * sizeof(unsigned char));
+-	return infostuff->target;
++	return info->target;
+ }
+ 
+ static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+    const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+-	struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++	struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+ 
+-	if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
+-	   infostuff->target == EBT_RETURN)
++	if (BASE_CHAIN && info->target == EBT_RETURN)
+ 		return -EINVAL;
+-	hookmask &= ~(1 << NF_BR_NUMHOOKS);
++	CLEAR_BASE_CHAIN_BIT;
+ 	if ( (strcmp(tablename, "nat") ||
+ 	   (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ 	   (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ 		return -EINVAL;
+ 	if (datalen != sizeof(struct ebt_nat_info))
+ 		return -EINVAL;
+-	if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++	if (INVALID_TARGET)
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.19-rc1/net/bridge/netfilter/ebtables.c	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/net/bridge/netfilter/ebtables.c	Sat Aug 31 12:59:08 2002
+@@ -22,10 +22,6 @@
+ #include <linux/kmod.h>
+ #include <linux/module.h>
+ #include <linux/vmalloc.h>
+-#include <linux/skbuff.h>
+-#include <linux/if_ether.h>
+-#include <linux/netfilter_bridge.h>
+-#include <linux/netfilter_ipv4.h>
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/spinlock.h>
+ #include <asm/uaccess.h>
+@@ -40,6 +36,21 @@
+ #include <linux/netfilter_ipv4/listhelp.h>
+ 
+ #if 0 // use this for remote debugging
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++	struct tty_struct *my_tty;
++
++	/* The tty for the current task */
++	my_tty = current->tty;
++	if (my_tty != NULL) {
++		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++	}
++}
++
+ #define BUGPRINT(args) print_string(args);
+ #else
+ #define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+@@ -65,8 +76,6 @@
+ 
+ 
+ 
+-static void print_string(char *str);
+-
+ static DECLARE_MUTEX(ebt_mutex);
+ static LIST_HEAD(ebt_tables);
+ static LIST_HEAD(ebt_targets);
+@@ -78,20 +87,20 @@
+ 
+ static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+    const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const struct ebt_counter *c)
++   const struct net_device *out)
+ {
+ 	w->u.watcher->watcher(skb, in, out, w->data,
+-	   w->watcher_size, c);
++	   w->watcher_size);
+ 	// watchers don't give a verdict
+ 	return 0;
+ }
+ 
+ static inline int ebt_do_match (struct ebt_entry_match *m,
+    const struct sk_buff *skb, const struct net_device *in,
+-   const struct net_device *out, const struct ebt_counter *c)
++   const struct net_device *out)
+ {
+ 	return m->u.match->match(skb, in, out, m->data,
+-	   m->match_size, c);
++	   m->match_size);
+ }
+ 
+ static inline int ebt_dev_check(char *entry, const struct net_device *device)
+@@ -100,48 +109,48 @@
+ 		return 0;
+ 	if (!device)
+ 		return 1;
+-	return !!strncmp(entry, device->name, IFNAMSIZ);
++	return !!strcmp(entry, device->name);
+ }
+ 
+-#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+ // process standard matches
+-static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+    const struct net_device *in, const struct net_device *out)
+ {
+ 	int verdict, i;
+ 
+-	if (p->bitmask & EBT_802_3) {
+-		if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++	if (e->bitmask & EBT_802_3) {
++		if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+ 			return 1;
+-	} else if (!(p->bitmask & EBT_NOPROTO) &&
+-	   FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++	} else if (!(e->bitmask & EBT_NOPROTO) &&
++	   FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+ 		return 1;
+ 
+-	if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++	if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+ 		return 1;
+-	if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++	if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+ 		return 1;
+-	if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
+-	   p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++	if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+ 		return 1;
+-	if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
+-	   (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++	if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
++	   e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+ 		return 1;
+-	
+-	if (p->bitmask & EBT_SOURCEMAC) {
++
++	if (e->bitmask & EBT_SOURCEMAC) {
+ 		verdict = 0;
+ 		for (i = 0; i < 6; i++)
+-			verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
+-			   p->sourcemsk[i];
+-		if (FWINV(verdict != 0, EBT_ISOURCE) )
++			verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
++			   e->sourcemsk[i];
++		if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ 			return 1;
+ 	}
+-	if (p->bitmask & EBT_DESTMAC) {
++	if (e->bitmask & EBT_DESTMAC) {
+ 		verdict = 0;
+ 		for (i = 0; i < 6; i++)
+-			verdict |= (h->h_dest[i] ^ p->destmac[i]) &
+-			   p->destmsk[i];
+-		if (FWINV(verdict != 0, EBT_IDEST) )
++			verdict |= (h->h_dest[i] ^ e->destmac[i]) &
++			   e->destmsk[i];
++		if (FWINV2(verdict != 0, EBT_IDEST) )
+ 			return 1;
+ 	}
+ 	return 0;
+@@ -163,7 +172,7 @@
+ 	struct ebt_table_info *private = table->private;
+ 
+ 	read_lock_bh(&table->lock);
+-	cb_base = COUNTER_BASE(private->counters, private->nentries, \
++	cb_base = COUNTER_BASE(private->counters, private->nentries,
+ 	   cpu_number_map(smp_processor_id()));
+ 	if (private->chainstack)
+ 		cs = private->chainstack[cpu_number_map(smp_processor_id())];
+@@ -180,8 +189,7 @@
+ 		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
+ 			goto letscontinue;
+ 
+-		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
+-		   out, counter_base + i) != 0)
++		if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+ 			goto letscontinue;
+ 
+ 		// increase counter
+@@ -190,7 +198,7 @@
+ 		// these should only watch: not modify, nor tell us
+ 		// what to do with the packet
+ 		EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+-		   out, counter_base + i);
++		   out);
+ 
+ 		t = (struct ebt_entry_target *)
+ 		   (((char *)point) + point->target_offset);
+@@ -210,11 +218,13 @@
+ 		}
+ 		if (verdict == EBT_RETURN) {
+ letsreturn:
++#ifdef CONFIG_NETFILTER_DEBUG
+ 			if (sp == 0) {
+ 				BUGPRINT("RETURN on base chain");
+ 				// act like this is EBT_CONTINUE
+ 				goto letscontinue;
+ 			}
++#endif
+ 			sp--;
+ 			// put all the local variables right
+ 			i = cs[sp].n;
+@@ -227,11 +237,13 @@
+ 		}
+ 		if (verdict == EBT_CONTINUE)
+ 			goto letscontinue;
++#ifdef CONFIG_NETFILTER_DEBUG
+ 		if (verdict < 0) {
+ 			BUGPRINT("bogus standard verdict\n");
+ 			read_unlock_bh(&table->lock);
+ 			return NF_DROP;
+ 		}
++#endif
+ 		// jump to a udc
+ 		cs[sp].n = i + 1;
+ 		cs[sp].chaininfo = chaininfo;
+@@ -239,11 +251,13 @@
+ 		   (((char *)point) + point->next_offset);
+ 		i = 0;
+ 		chaininfo = (struct ebt_entries *) (base + verdict);
++#ifdef CONFIG_NETFILTER_DEBUG
+ 		if (chaininfo->distinguisher) {
+ 			BUGPRINT("jump to non-chain\n");
+ 			read_unlock_bh(&table->lock);
+ 			return NF_DROP;
+ 		}
++#endif
+ 		nentries = chaininfo->nentries;
+ 		point = (struct ebt_entry *)chaininfo->data;
+ 		counter_base = cb_base + chaininfo->counter_offset;
+@@ -266,12 +280,10 @@
+ 	return NF_DROP;
+ }
+ 
+-/* If it succeeds, returns element and locks mutex */
++// If it succeeds, returns element and locks mutex
+ static inline void *
+-find_inlist_lock_noload(struct list_head *head,
+-			const char *name,
+-			int *error,
+-			struct semaphore *mutex)
++find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
++   struct semaphore *mutex)
+ {
+ 	void *ret;
+ 
+@@ -291,11 +303,8 @@
+ #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+ #else
+ static void *
+-find_inlist_lock(struct list_head *head,
+-		 const char *name,
+-		 const char *prefix,
+-		 int *error,
+-		 struct semaphore *mutex)
++find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
++   int *error, struct semaphore *mutex)
+ {
+ 	void *ret;
+ 
+@@ -345,7 +354,6 @@
+ 	if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+ 	   ((char *)e) + e->watchers_offset)
+ 		return -EINVAL;
+-	m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+ 	if (!match)
+ 		return ret;
+@@ -374,7 +382,6 @@
+ 	if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+ 	   ((char *)e) + e->target_offset)
+ 		return -EINVAL;
+-	w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+ 	if (!watcher)
+ 		return ret;
+@@ -457,7 +464,7 @@
+ 	// a plain old entry, heh
+ 	if (sizeof(struct ebt_entry) > e->watchers_offset ||
+ 	   e->watchers_offset > e->target_offset ||
+-	   e->target_offset > e->next_offset) {
++	   e->target_offset >= e->next_offset) {
+ 		BUGPRINT("entry offsets not in right order\n");
+ 		return -EINVAL;
+ 	}
+@@ -537,6 +544,27 @@
+ }
+ 
+ static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++	struct ebt_entry_target *t;
++
++	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++		return 0;
++	// we're done
++	if (cnt && (*cnt)-- == 0)
++		return 1;
++	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++	if (t->u.target->destroy)
++		t->u.target->destroy(t->data, t->target_size);
++	if (t->u.target->me)
++		__MOD_DEC_USE_COUNT(t->u.target->me);
++
++	return 0;
++}
++
++static inline int
+ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+    const char *name, unsigned int *cnt, unsigned int valid_hooks,
+    struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+@@ -562,10 +590,6 @@
+ 		BUGPRINT("NOPROTO & 802_3 not allowed\n");
+ 		return -EINVAL;
+ 	}
+-	e->in[IFNAMSIZ - 1] = '\0';
+-	e->out[IFNAMSIZ - 1] = '\0';
+-	e->logical_in[IFNAMSIZ - 1] = '\0';
+-	e->logical_out[IFNAMSIZ - 1] = '\0';
+ 	// what hook do we belong to?
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if ((valid_hooks & (1 << i)) == 0)
+@@ -597,7 +621,6 @@
+ 	if (ret != 0)
+ 		goto cleanup_watchers;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-	t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 	target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+ 	if (!target)
+ 		goto cleanup_watchers;
+@@ -637,27 +660,6 @@
+ 	return ret;
+ }
+ 
+-static inline int
+-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+-{
+-	struct ebt_entry_target *t;
+-
+-	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+-		return 0;
+-	// we're done
+-	if (cnt && (*cnt)-- == 0)
+-		return 1;
+-	EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+-	EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+-	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-	if (t->u.target->destroy)
+-		t->u.target->destroy(t->data, t->target_size);
+-	if (t->u.target->me)
+-		__MOD_DEC_USE_COUNT(t->u.target->me);
+-
+-	return 0;
+-}
+-
+ // checks for loops and sets the hook mask for udc
+ // the hook mask for udc tells us from which base chains the udc can be
+ // accessed. This mask is a parameter to the check() functions of the extensions
+@@ -687,7 +689,6 @@
+ 		}
+ 		t = (struct ebt_entry_target *)
+ 		   (((char *)e) + e->target_offset);
+-		t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ 		if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+ 			goto letscontinue;
+ 		if (e->target_offset + sizeof(struct ebt_standard_target) >
+@@ -857,7 +858,6 @@
+ 	//   beginning of a chain. This can only occur in chains that
+ 	//   are not accessible from any base chains, so we don't care.
+ 
+-	repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	// used to know what we need to clean up if something goes wrong
+ 	i = 0;
+ 	ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+@@ -961,7 +961,7 @@
+ 	// the table doesn't like it
+ 	if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+ 		goto free_unlock;
+-		
++
+ 	if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+ 		BUGPRINT("Wrong nr. of counters requested\n");
+ 		ret = -EINVAL;
+@@ -979,8 +979,8 @@
+ 	t->private = newinfo;
+ 	write_unlock_bh(&t->lock);
+ 	up(&ebt_mutex);
+-	// So, a user can change the chains while having messed up his counter
+-	// allocation. Only reason why I do this is because this way the lock
++	// So, a user can change the chains while having messed up her counter
++	// allocation. Only reason why this is done is because this way the lock
+ 	// is held only once, while this doesn't bring the kernel into a
+ 	// dangerous state.
+ 	if (tmp.num_counters &&
+@@ -1220,11 +1220,10 @@
+ 
+ 	if ( !(tmp = (struct ebt_counter *)
+ 	   vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+-		MEMPRINT("Updata_counters && nomemory\n");
++		MEMPRINT("Update_counters && nomemory\n");
+ 		return -ENOMEM;
+ 	}
+ 
+-	hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ 	t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ 	if (!t)
+ 		goto free_tmp;
+@@ -1279,12 +1278,13 @@
+ static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+ {
+ 	int ret;
+-	char *hlp = ubase - base + (char *)e + e->target_offset;
++	char *hlp;
+ 	struct ebt_entry_target *t;
+ 
+ 	if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ 		return 0;
+ 
++	hlp = ubase - base + (char *)e + e->target_offset;
+ 	t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ 	
+ 	ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+@@ -1369,10 +1369,6 @@
+ 		BUGPRINT("Couldn't copy entries to userspace\n");
+ 		return -EFAULT;
+ 	}
+-	if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
+-		BUGPRINT("Couldn't copy ebt_replace to userspace\n");
+-		return -EFAULT;
+-	}
+ 	// set the match/watcher/target names right
+ 	return EBT_ENTRY_ITERATE(entries, entries_size,
+ 	   ebt_make_names, entries, tmp.entries);
+@@ -1454,21 +1450,6 @@
+     EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+ };
+ 
+-// Copyright (C) 1998 by Ori Pomerantz
+-// Print the string to the appropriate tty, the one
+-// the current task uses
+-static void print_string(char *str)
+-{
+-	struct tty_struct *my_tty;
+-
+-	/* The tty for the current task */
+-	my_tty = current->tty;
+-	if (my_tty != NULL) {
+-		(*(my_tty->driver).write)(my_tty, 0, str, strlen(str));  
+-		(*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
+-	}
+-}
+-
+ static int __init init(void)
+ {
+ 	int ret;
+@@ -1479,14 +1460,14 @@
+ 	if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+ 		return ret;
+ 
+-	print_string("Ebtables v2.0 registered");
++	printk("Ebtables v2.0 registered");
+ 	return 0;
+ }
+ 
+ static void __exit fini(void)
+ {
+ 	nf_unregister_sockopt(&ebt_sockopts);
+-	print_string("Ebtables v2.0 unregistered");
++	printk("Ebtables v2.0 unregistered");
+ }
+ 
+ EXPORT_SYMBOL(ebt_register_table);
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebtables.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebtables.h	Sat Aug 31 12:59:08 2002
+@@ -40,9 +40,13 @@
+ #define EBT_RETURN   -4
+ #define NUM_STANDARD_TARGETS   4
+ 
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
+ struct ebt_counter
+ {
+-	__u64 pcnt;
++	uint64_t pcnt;
+ };
+ 
+ struct ebt_entries {
+@@ -135,7 +139,7 @@
+ 	// this needs to be the first field
+ 	unsigned int bitmask;
+ 	unsigned int invflags;
+-	__u16 ethproto;
++	uint16_t ethproto;
+ 	// the physical in-dev
+ 	char in[IFNAMSIZ];
+ 	// the logical in-dev
+@@ -183,7 +187,7 @@
+ 	// 0 == it matches
+ 	int (*match)(const struct sk_buff *skb, const struct net_device *in,
+ 	   const struct net_device *out, const void *matchdata,
+-	   unsigned int datalen, const struct ebt_counter *c);
++	   unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+@@ -197,7 +201,7 @@
+ 	const char name[EBT_FUNCTION_MAXNAMELEN];
+ 	void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
+ 	   const struct net_device *out, const void *watcherdata,
+-	   unsigned int datalen, const struct ebt_counter *c);
++	   unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+@@ -210,12 +214,9 @@
+ 	struct list_head list;
+ 	const char name[EBT_FUNCTION_MAXNAMELEN];
+ 	// returns one of the standard verdicts
+-	int (*target)(struct sk_buff **pskb,
+-	       unsigned int hooknr,
+-	       const struct net_device *in,
+-	       const struct net_device *out,
+-	       const void *targetdata,
+-	       unsigned int datalen);
++	int (*target)(struct sk_buff **pskb, unsigned int hooknr,
++	   const struct net_device *in, const struct net_device *out,
++	   const void *targetdata, unsigned int datalen);
+ 	// 0 == let it in
+ 	int (*check)(const char *tablename, unsigned int hookmask,
+ 	   const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+@@ -271,6 +272,16 @@
+    const struct net_device *in, const struct net_device *out,
+    struct ebt_table *table);
+ 
++   // Used in the kernel match() functions
++#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
++// True if the hook mask denotes that the rule is in a base chain,
++// used in the check() functions
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
++// True if the target is not a standard target
++#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target >= 0)
++
+ #endif /* __KERNEL__ */
+ 
+ // blatently stolen from ip_tables.h
+@@ -333,9 +344,9 @@
+ 		if (__ret != 0)                             \
+ 			break;                              \
+ 		if (__entry->bitmask != 0)                  \
+-		 __i += __entry->next_offset;               \
++			__i += __entry->next_offset;        \
+ 		else                                        \
+-		 __i += sizeof(struct ebt_entries);         \
++			__i += sizeof(struct ebt_entries);  \
+ 	}                                                   \
+ 	if (__ret == 0) {                                   \
+ 		if (__i != (size))                          \
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_arp.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_arp.h	Sat Aug 31 12:59:08 2002
+@@ -12,15 +12,15 @@
+ 
+ struct ebt_arp_info
+ {
+-	__u16 htype;
+-	__u16 ptype;
+-	__u16 opcode;
+-	__u32 saddr;
+-	__u32 smsk;
+-	__u32 daddr;
+-	__u32 dmsk;
+-	__u8  bitmask;
+-	__u8  invflags;
++	uint16_t htype;
++	uint16_t ptype;
++	uint16_t opcode;
++	uint32_t saddr;
++	uint32_t smsk;
++	uint32_t daddr;
++	uint32_t dmsk;
++	uint8_t  bitmask;
++	uint8_t  invflags;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_ip.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_ip.h	Sat Aug 31 12:59:08 2002
+@@ -11,14 +11,14 @@
+ // the same values are used for the invflags
+ struct ebt_ip_info
+ {
+-	__u32 saddr;
+-	__u32 daddr;
+-	__u32 smsk;
+-	__u32 dmsk;
+-	__u8  tos;
+-	__u8  protocol;
+-	__u8  bitmask;
+-	__u8  invflags;
++	uint32_t saddr;
++	uint32_t daddr;
++	uint32_t smsk;
++	uint32_t dmsk;
++	uint8_t  tos;
++	uint8_t  protocol;
++	uint8_t  bitmask;
++	uint8_t  invflags;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_vlan.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_vlan.h	Sat Aug 31 12:59:08 2002
+@@ -8,12 +8,12 @@
+ #define EBT_VLAN_MATCH "vlan"
+ 
+ struct ebt_vlan_info {
+-	__u16 id;		/* VLAN ID {1-4095} */
+-	__u8 prio;		/* VLAN User Priority {0-7} */
+-	__u16 encap;		/* VLAN Encapsulated frame code {0-65535} */
+-	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg, 
++	uint16_t id;		/* VLAN ID {1-4095} */
++	uint8_t prio;		/* VLAN User Priority {0-7} */
++	uint16_t encap;		/* VLAN Encapsulated frame code {0-65535} */
++	uint8_t bitmask;		/* Args bitmask bit 1=1 - ID arg,
+ 				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+-	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
++	uint8_t invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+ 				   bit 2=1 - inversed Pirority arg */
+ };
+ 
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_log.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_log.h	Sat Aug 31 12:59:08 2002
+@@ -9,9 +9,9 @@
+ 
+ struct ebt_log_info
+ {
+-	__u8 loglevel;
+-	__u8 prefix[EBT_LOG_PREFIX_SIZE];
+-	__u32 bitmask;
++	uint8_t loglevel;
++	uint8_t prefix[EBT_LOG_PREFIX_SIZE];
++	uint32_t bitmask;
+ };
+ 
+ #endif
+--- linux-2.4.19-rc1/include/linux/netfilter_bridge/ebt_mark_m.h	Sat Aug 31 12:53:52 2002
++++ linux-2.4.20-pre5-rc2/include/linux/netfilter_bridge/ebt_mark_m.h	Sat Aug 31 12:59:08 2002
+@@ -7,8 +7,8 @@
+ struct ebt_mark_m_info
+ {
+ 	unsigned long mark, mask;
+-	__u8 invert;
+-	__u8 bitmask;
++	uint8_t invert;
++	uint8_t bitmask;
+ };
+ #define EBT_MARK_MATCH "mark_m"
+ 
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5.001.diff
new file mode 100644
index 0000000..f10983a
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.20-pre5.001.diff
@@ -0,0 +1,315 @@
+--- linux-2.4.20-pre5-old/include/linux/if_bridge.h	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/include/linux/if_bridge.h	Wed Sep 18 21:01:01 2002
+@@ -103,12 +103,8 @@
+ 
+ extern int (*br_ioctl_hook)(unsigned long arg);
+ extern int (*br_handle_frame_hook)(struct sk_buff *skb);
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
+-   const struct net_device *in, const struct net_device *out,
+-   int (*okfn)(struct sk_buff *));
+-#endif
++extern int (*br_should_route_hook)(struct sk_buff **pskb);
++
+ #endif
+ 
+ #endif
+--- linux-2.4.20-pre5-old/net/core/dev.c	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/core/dev.c	Wed Sep 18 21:01:01 2002
+@@ -1473,6 +1473,7 @@
+ 		ret = handle_bridge(skb, pt_prev);
+ 		if (br_handle_frame_hook(skb) == 0)
+ 			return ret;
++		pt_prev = NULL;
+ 	}
+ #endif
+ 
+--- linux-2.4.20-pre5-old/net/bridge/br_input.c	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br_input.c	Wed Sep 18 21:01:01 2002
+@@ -19,15 +19,14 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-#include <linux/netfilter.h>
+-#endif
+ 
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+ 
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+ {
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	netif_rx(skb);
+ 
+ 	return 0;
+@@ -150,12 +149,9 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-		if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
+-		   skb->dev, NULL, NULL) == NF_DROP)
++		if (br_should_route_hook && br_should_route_hook(&skb))
+ 			return -1;
+-#endif
++
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
+ 		read_unlock(&br->lock);
+--- linux-2.4.20-pre5-old/net/bridge/br_forward.c	Sat Aug  3 02:39:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br_forward.c	Wed Sep 18 21:01:01 2002
+@@ -49,6 +49,9 @@
+ static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
+ {
+ 	skb->dev = to->dev;
++#ifdef CONFIG_NETFILTER_DEBUG
++	skb->nf_debug = 0;
++#endif
+ 	NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ 			__br_forward_finish);
+ }
+--- linux-2.4.20-pre5-old/net/bridge/br.c	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/br.c	Wed Sep 18 21:01:01 2002
+@@ -28,13 +28,7 @@
+ #include "../atm/lec.h"
+ #endif
+ 
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
+-                        const struct net_device *in,
+-                        const struct net_device *out,
+-                        int (*okfn)(struct sk_buff *)) = NULL;
+-#endif
++int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+ 
+ void br_dec_use_count()
+ {
+@@ -82,12 +76,7 @@
+ #endif
+ }
+ 
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+-    defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-EXPORT_SYMBOL(broute_decision);
+-#else
+-EXPORT_NO_SYMBOLS;
+-#endif
++EXPORT_SYMBOL(br_should_route_hook);
+ 
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux-2.4.20-pre5-old/net/bridge/Makefile	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/Makefile	Wed Sep 18 21:01:01 2002
+@@ -7,11 +7,7 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
+-ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
+-ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
+ export-objs := br.o
+-endif
+-endif
+ 
+ O_TARGET	:= bridge.o
+ obj-y		:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+--- linux-2.4.20-pre5-old/include/linux/netfilter_bridge.h	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge.h	Wed Sep 18 21:01:01 2002
+@@ -23,13 +23,13 @@
+ #define NF_BR_NUMHOOKS		6
+ 
+ enum nf_br_hook_priorities {
+-        NF_BR_PRI_FIRST = INT_MIN,
+-        NF_BR_PRI_FILTER_BRIDGED = -200,
+-        NF_BR_PRI_FILTER_OTHER = 200,
+-        NF_BR_PRI_NAT_DST_BRIDGED = -300,
+-        NF_BR_PRI_NAT_DST_OTHER = 100,
+-        NF_BR_PRI_NAT_SRC = 300,
+-        NF_BR_PRI_LAST = INT_MAX,
++	NF_BR_PRI_FIRST = INT_MIN,
++	NF_BR_PRI_FILTER_BRIDGED = -200,
++	NF_BR_PRI_FILTER_OTHER = 200,
++	NF_BR_PRI_NAT_DST_BRIDGED = -300,
++	NF_BR_PRI_NAT_DST_OTHER = 100,
++	NF_BR_PRI_NAT_SRC = 300,
++	NF_BR_PRI_LAST = INT_MAX,
+ };
+ 
+ #endif
+--- linux-2.4.20-pre5-old/net/bridge/netfilter/Makefile	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/Makefile	Wed Sep 18 21:01:01 2002
+@@ -11,7 +11,7 @@
+ 
+ export-objs := ebtables.o
+ 
+-obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+ obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+ obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+--- linux-2.4.20-pre5-old/net/bridge/netfilter/Config.in	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/Config.in	Wed Sep 18 21:01:01 2002
+@@ -1,17 +1,16 @@
+ #
+ # Bridge netfilter configuration
+ #
+-dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
+-dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
+-dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
+-
++dep_tristate '  Bridge: ebtables' CONFIG_BRIDGE_NF_EBTABLES $CONFIG_BRIDGE
++dep_tristate '    ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_NF_EBTABLES
++dep_tristate '    ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_NF_EBTABLES
+--- linux-2.4.20-pre5-old/net/bridge/netfilter/ebtable_broute.c	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebtable_broute.c	Wed Sep 18 21:01:01 2002
+@@ -40,11 +40,15 @@
+   RW_LOCK_UNLOCKED, check, NULL
+ };
+ 
+-static unsigned int
+-ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device *in,
+-   const struct net_device *out, int (*okfn)(struct sk_buff *))
++static int ebt_broute(struct sk_buff **pskb)
+ {
+-	return ebt_do_table(hook, pskb, in, out, &broute_table);
++	int ret;
++
++	ret = ebt_do_table(NF_BR_BROUTING, pskb, (*pskb)->dev, NULL,
++	   &broute_table);
++	if (ret == NF_DROP)
++		return 1; // route it
++	return 0; // bridge it
+ }
+ 
+ static int __init init(void)
+@@ -55,8 +59,8 @@
+ 	if (ret < 0)
+ 		return ret;
+ 	br_write_lock_bh(BR_NETPROTO_LOCK);
+-	// in br_input.c, br_handle_frame() wants to call broute_decision()
+-	broute_decision = ebt_broute;
++	// see br_input.c
++	br_should_route_hook = ebt_broute;
+ 	br_write_unlock_bh(BR_NETPROTO_LOCK);
+ 	return ret;
+ }
+@@ -64,7 +68,7 @@
+ static void __exit fini(void)
+ {
+ 	br_write_lock_bh(BR_NETPROTO_LOCK);
+-	broute_decision = NULL;
++	br_should_route_hook = NULL;
+ 	br_write_unlock_bh(BR_NETPROTO_LOCK);
+ 	ebt_unregister_table(&broute_table);
+ }
+--- linux-2.4.20-pre5-old/net/bridge/netfilter/ebt_vlan.c	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/net/bridge/netfilter/ebt_vlan.c	Wed Sep 18 21:01:01 2002
+@@ -35,7 +35,7 @@
+ MODULE_LICENSE ("GPL");
+ 
+ 
+-#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": "  __VA_ARGS__)
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __VA_ARGS__)
+ #define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+ #define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+ #define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+--- linux-2.4.20-pre5-old/include/linux/netfilter_bridge/ebtables.h	Wed Sep 18 21:04:46 2002
++++ linux-2.4.20-pre5-ebtables/include/linux/netfilter_bridge/ebtables.h	Wed Sep 18 21:01:01 2002
+@@ -4,7 +4,7 @@
+  *	Authors:
+  *	Bart De Schuymer		<bart.de.schuymer@pandora.be>
+  *
+- *  ebtables.c,v 2.0, April, 2002
++ *  ebtables.c,v 2.0, September, 2002
+  *
+  *  This code is stongly inspired on the iptables code which is
+  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+@@ -20,19 +20,6 @@
+ #define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ 
+-// [gs]etsockopt numbers
+-#define EBT_BASE_CTL            128
+-
+-#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+-#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+-#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+-
+-#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+-#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+-#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+-#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+-#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+-
+ // verdicts >0 are "branches"
+ #define EBT_ACCEPT   -1
+ #define EBT_DROP     -2
+@@ -40,10 +27,6 @@
+ #define EBT_RETURN   -4
+ #define NUM_STANDARD_TARGETS   4
+ 
+-// return values for match() functions
+-#define EBT_MATCH 0
+-#define EBT_NOMATCH 1
+-
+ struct ebt_counter
+ {
+ 	uint64_t pcnt;
+@@ -180,6 +163,23 @@
+ 
+ #ifdef __KERNEL__
+ 
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL            128
++
++#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++// return values for match() functions
++#define EBT_MATCH 0
++#define EBT_NOMATCH 1
++
+ struct ebt_match
+ {
+ 	struct list_head list;
+@@ -242,7 +242,7 @@
+ 	// room to maintain the stack used for jumping from and into udc
+ 	struct ebt_chainstack **chainstack;
+ 	char *entries;
+-	struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
++	struct ebt_counter counters[0] ____cacheline_aligned;
+ };
+ 
+ struct ebt_table
diff --git a/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.001.diff b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.001.diff
new file mode 100644
index 0000000..3c869ce
--- /dev/null
+++ b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.001_vs_2.4.20-pre7.001.diff
@@ -0,0 +1,14 @@
+--- linux-2.4.20-pre7/net/bridge/br_input.c	Sun Sep 29 21:34:42 2002
++++ linux-2.4.20-pre7-ebt/net/bridge/br_input.c	Sun Sep 29 21:24:37 2002
+@@ -149,8 +149,10 @@
+ 		goto handle_special_frame;
+ 
+ 	if (p->state == BR_STATE_FORWARDING) {
+-		if (br_should_route_hook && br_should_route_hook(&skb))
++		if (br_should_route_hook && br_should_route_hook(&skb)) {
++			read_unlock(&br->lock);
+ 			return -1;
++		}
+ 
+ 		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ 			br_handle_frame_finish);
diff --git a/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.001.diff b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.001.diff
new file mode 100644
index 0000000..9bfafcc
--- /dev/null
+++ b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.002_vs_2.4.20-pre7.001.diff
@@ -0,0 +1,187 @@
+--- linux-2.4.20-pre7/net/bridge/netfilter/ebt_ip.c	Thu Oct 17 23:35:25 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebt_ip.c	Thu Oct 17 23:22:58 2002
+@@ -6,13 +6,28 @@
+  *
+  *  April, 2002
+  *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
+  */
+ 
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/netfilter_bridge/ebt_ip.h>
+ #include <linux/ip.h>
++#include <linux/in.h>
+ #include <linux/module.h>
+ 
++struct tcpudphdr {
++	uint16_t src;
++	uint16_t dst;
++};
++
++union h_u {
++	unsigned char *raw;
++	struct tcpudphdr *tuh;
++};
++
+ static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device *in,
+    const struct net_device *out, const void *data,
+    unsigned int datalen)
+@@ -22,9 +37,31 @@
+ 	if (info->bitmask & EBT_IP_TOS &&
+ 	   FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
+ 		return EBT_NOMATCH;
+-	if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
+-	   ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
+-		return EBT_NOMATCH;
++	if (info->bitmask & EBT_IP_PROTO) {
++		if (FWINV(info->protocol != ((*skb).nh.iph)->protocol,
++		          EBT_IP_PROTO))
++			return EBT_NOMATCH;
++		if ( info->protocol == IPPROTO_TCP ||
++		     info->protocol == IPPROTO_UDP )
++		{
++			union h_u h;
++			h.raw = skb->data + skb->nh.iph->ihl*4;
++			if (info->bitmask & EBT_IP_DPORT) {
++				uint16_t port = ntohs(h.tuh->dst);
++				if (FWINV(port < info->dport[0] ||
++				          port > info->dport[1],
++				          EBT_IP_DPORT))
++				return EBT_NOMATCH;
++			}
++			if (info->bitmask & EBT_IP_SPORT) {
++				uint16_t port = ntohs(h.tuh->src);
++				if (FWINV(port < info->sport[0] ||
++				          port > info->sport[1],
++				          EBT_IP_SPORT))
++				return EBT_NOMATCH;
++			}
++		}
++	}
+ 	if (info->bitmask & EBT_IP_SOURCE &&
+ 	   FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
+ 	   info->saddr, EBT_IP_SOURCE))
+@@ -47,6 +84,17 @@
+ 	   e->invflags & EBT_IPROTO)
+ 		return -EINVAL;
+ 	if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
++		return -EINVAL;
++	if (info->bitmask & (EBT_IP_DPORT | EBT_IP_SPORT)) {
++		if (!info->bitmask & EBT_IPROTO)
++			return -EINVAL;
++		if (info->protocol != IPPROTO_TCP &&
++		    info->protocol != IPPROTO_UDP)
++			 return -EINVAL;
++	}
++	if (info->bitmask & EBT_IP_DPORT && info->dport[0] > info->dport[1])
++		return -EINVAL;
++	if (info->bitmask & EBT_IP_SPORT && info->sport[0] > info->sport[1])
+ 		return -EINVAL;
+ 	return 0;
+ }
+--- linux-2.4.20-pre7/net/bridge/netfilter/ebtables.c	Thu Oct 17 23:35:25 2002
++++ linux-2.4.20-pre7-bcnt/net/bridge/netfilter/ebtables.c	Thu Oct 17 21:58:08 2002
+@@ -194,6 +194,7 @@
+ 
+ 		// increase counter
+ 		(*(counter_base + i)).pcnt++;
++		(*(counter_base + i)).bcnt+=(**pskb).len;
+ 
+ 		// these should only watch: not modify, nor tell us
+ 		// what to do with the packet
+@@ -885,8 +886,10 @@
+ 	// add other counters to those of cpu 0
+ 	for (cpu = 1; cpu < smp_num_cpus; cpu++) {
+ 		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+-		for (i = 0; i < nentries; i++)
++		for (i = 0; i < nentries; i++) {
+ 			counters[i].pcnt += counter_base[i].pcnt;
++			counters[i].bcnt += counter_base[i].bcnt;
++		}
+ 	}
+ }
+ 
+@@ -1245,8 +1248,10 @@
+ 	write_lock_bh(&t->lock);
+ 
+ 	// we add to the counters of the first cpu
+-	for (i = 0; i < hlp.num_counters; i++)
++	for (i = 0; i < hlp.num_counters; i++) {
+ 		t->private->counters[i].pcnt += tmp[i].pcnt;
++		t->private->counters[i].bcnt += tmp[i].bcnt;
++	}
+ 
+ 	write_unlock_bh(&t->lock);
+ 	ret = 0;
+--- linux-2.4.20-pre7/include/linux/netfilter_bridge/ebtables.h	Thu Oct 17 23:35:25 2002
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebtables.h	Thu Oct 17 22:41:32 2002
+@@ -30,6 +30,7 @@
+ struct ebt_counter
+ {
+ 	uint64_t pcnt;
++	uint64_t bcnt;
+ };
+ 
+ struct ebt_entries {
+@@ -161,8 +162,6 @@
+ 	char *entries;
+ };
+ 
+-#ifdef __KERNEL__
+-
+ // [gs]etsockopt numbers
+ #define EBT_BASE_CTL            128
+ 
+@@ -175,6 +174,8 @@
+ #define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+ #define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+ #define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
++
++#ifdef __KERNEL__
+ 
+ // return values for match() functions
+ #define EBT_MATCH 0
+--- linux-2.4.20-pre7/include/linux/netfilter_bridge/ebt_ip.h	Thu Oct 17 23:35:25 2002
++++ linux-2.4.20-pre7-bcnt/include/linux/netfilter_bridge/ebt_ip.h	Thu Oct 17 23:22:45 2002
+@@ -1,3 +1,17 @@
++/*
++ *  ebt_ip
++ *
++ *	Authors:
++ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  April, 2002
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ */
++
+ #ifndef __LINUX_BRIDGE_EBT_IP_H
+ #define __LINUX_BRIDGE_EBT_IP_H
+ 
+@@ -5,7 +19,10 @@
+ #define EBT_IP_DEST 0x02
+ #define EBT_IP_TOS 0x04
+ #define EBT_IP_PROTO 0x08
+-#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_SPORT 0x10
++#define EBT_IP_DPORT 0x20
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
++ EBT_IP_SPORT | EBT_IP_DPORT )
+ #define EBT_IP_MATCH "ip"
+ 
+ // the same values are used for the invflags
+@@ -19,6 +36,8 @@
+ 	uint8_t  protocol;
+ 	uint8_t  bitmask;
+ 	uint8_t  invflags;
++	uint16_t sport[2];
++	uint16_t dport[2];
+ };
+ 
+ #endif
diff --git a/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.001.diff b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.001.diff
new file mode 100644
index 0000000..245d689
--- /dev/null
+++ b/kernel/patches/incremental-patches/v2.0/ebtables-v2.0.003_vs_2.4.20.001.diff
@@ -0,0 +1,357 @@
+--- linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebt_vlan.c	Sat Dec  7 14:55:44 2002
++++ linux-2.4.20-ebt/net/bridge/netfilter/ebt_vlan.c	Sat Dec  7 14:51:44 2002
+@@ -25,17 +25,17 @@
+ #include <linux/netfilter_bridge/ebt_vlan.h>
+ 
+ static unsigned char debug;
+-#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++#define MODULE_VERSION "0.6"
+ 
+-MODULE_PARM (debug, "0-1b");
+-MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
+-MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
+-MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
+-		    MODULE_VERSION);
+-MODULE_LICENSE ("GPL");
++MODULE_PARM(debug, "0-1b");
++MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION("802.1Q match module (ebtables extension), v"
++		   MODULE_VERSION);
++MODULE_LICENSE("GPL");
+ 
+ 
+-#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __VA_ARGS__)
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG "ebt_vlan: " __VA_ARGS__)
+ #define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+ #define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+ #define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+@@ -59,11 +59,10 @@
+  * 1 - miss (rule params not acceptable to the parsed frame)
+  */
+ static int
+-ebt_filter_vlan (const struct sk_buff *skb,
+-		 const struct net_device *in,
+-		 const struct net_device *out,
+-		 const void *data,
+-		 unsigned int datalen)
++ebt_filter_vlan(const struct sk_buff *skb,
++		const struct net_device *in,
++		const struct net_device *out,
++		const void *data, unsigned int datalen)
+ {
+ 	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;	/* userspace data */
+ 	struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw;	/* Passed tagged frame */
+@@ -75,62 +74,35 @@
+ 
+ 	/*
+ 	 * Tag Control Information (TCI) consists of the following elements:
+-	 * - User_priority. This field allows the tagged frame to carry user_priority
+-	 * information across Bridged LANs in which individual LAN segments may be unable to signal
+-	 * priority information (e.g., 802.3/Ethernet segments). 
+-	 * The user_priority field is three bits in length, 
+-	 * interpreted as a binary number. The user_priority is therefore
+-	 * capable of representing eight priority levels, 0 through 7. 
+-	 * The use and interpretation of this field is defined in ISO/IEC 15802-3.
+-	 * - Canonical Format Indicator (CFI). This field is used,
+-	 * in 802.3/Ethernet, to signal the presence or absence
+-	 * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
+-	 * in the RIF, to signal the bit order of address information carried in the encapsulated
+-	 * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
+-	 * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
+-	 * which the frame belongs. The twelve-bit VLAN Identifier (VID) field 
+-	 * uniquely identify the VLAN to which the frame belongs. 
+-	 * The VID is encoded as an unsigned binary number. 
+-	 */
+-	TCI = ntohs (frame->h_vlan_TCI);
+-	id = TCI & 0xFFF;
+-	prio = TCI >> 13;
++	 * - User_priority. The user_priority field is three bits in length, 
++	 * interpreted as a binary number. 
++	 * - Canonical Format Indicator (CFI). The Canonical Format Indicator 
++	 * (CFI) is a single bit flag value. Currently ignored.
++	 * - VLAN Identifier (VID). The VID is encoded as 
++	 * an unsigned binary number. 
++	 */
++	TCI = ntohs(frame->h_vlan_TCI);
++	id = TCI & VLAN_VID_MASK;
++	prio = (TCI >> 13) & 0x7;
+ 	encap = frame->h_vlan_encapsulated_proto;
+ 
+ 	/*
+-	 * First step is to check is null VLAN ID present
+-	 * in the parsed frame
++	 * Checking VLAN Identifier (VID)
+ 	 */
+-	if (!(id)) {
+-		/*
+-		 * Checking VLAN Identifier (VID)
+-		 */
+-		if (GET_BITMASK (EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
+-			EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
+-			DEBUG_MSG
+-			    ("matched rule id=%s%d for frame id=%d\n",
+-			     INV_FLAG (EBT_VLAN_ID), info->id, id);
+-		}
+-	} else {
+-		/*
+-		 * Checking user_priority
+-		 */
+-		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
+-			EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
+-			DEBUG_MSG
+-			    ("matched rule prio=%s%d for frame prio=%d\n",
+-			     INV_FLAG (EBT_VLAN_PRIO), info->prio,
+-			     prio);
+-		}
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* Is VLAN ID parsed? */
++		EXIT_ON_MISMATCH(id, EBT_VLAN_ID);
++	}
++	/*
++	 * Checking user_priority
++	 */
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {	/* Is VLAN user_priority parsed? */
++		EXIT_ON_MISMATCH(prio, EBT_VLAN_PRIO);
+ 	}
+ 	/*
+ 	 * Checking Encapsulated Proto (Length/Type) field
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
+-		EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
+-		DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
+-			   INV_FLAG (EBT_VLAN_ENCAP),
+-			   ntohs (info->encap), ntohs (encap));
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {	/* Is VLAN Encap parsed? */
++		EXIT_ON_MISMATCH(encap, EBT_VLAN_ENCAP);
+ 	}
+ 	/*
+ 	 * All possible extension parameters was parsed.
+@@ -141,7 +113,7 @@
+ 
+ /*
+  * Function description: ebt_vlan_check() is called when userspace 
+- * delivers the table to the kernel, 
++ * delivers the table entry to the kernel, 
+  * and to check that userspace doesn't give a bad table.
+  * Parameters:
+  * const char *tablename - table name string
+@@ -154,29 +126,29 @@
+  * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
+  */
+ static int
+-ebt_check_vlan (const char *tablename,
+-		unsigned int hooknr,
+-		const struct ebt_entry *e, void *data,
+-		unsigned int datalen)
++ebt_check_vlan(const char *tablename,
++	       unsigned int hooknr,
++	       const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ 	struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+ 
+ 	/*
+ 	 * Parameters buffer overflow check 
+ 	 */
+-	if (datalen != sizeof (struct ebt_vlan_info)) {
++	if (datalen != sizeof(struct ebt_vlan_info)) {
+ 		DEBUG_MSG
+-		    ("params size %d is not eq to ebt_vlan_info (%d)\n",
+-		     datalen, sizeof (struct ebt_vlan_info));
++		    ("passed size %d is not eq to ebt_vlan_info (%d)\n",
++		     datalen, sizeof(struct ebt_vlan_info));
+ 		return -EINVAL;
+ 	}
+ 
+ 	/*
+ 	 * Is it 802.1Q frame checked?
+ 	 */
+-	if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
+-		DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+-			   (unsigned short) ntohs (e->ethproto));
++	if (e->ethproto != __constant_htons(ETH_P_8021Q)) {
++		DEBUG_MSG
++		    ("passed entry proto %2.4X is not 802.1Q (8100)\n",
++		     (unsigned short) ntohs(e->ethproto));
+ 		return -EINVAL;
+ 	}
+ 
+@@ -185,8 +157,8 @@
+ 	 * True if even one bit is out of mask
+ 	 */
+ 	if (info->bitmask & ~EBT_VLAN_MASK) {
+-		DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
+-			   info->bitmask, EBT_VLAN_MASK);
++		DEBUG_MSG("bitmask %2X is out of mask (%2X)\n",
++			  info->bitmask, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -194,91 +166,62 @@
+ 	 * Check for inversion flags range 
+ 	 */
+ 	if (info->invflags & ~EBT_VLAN_MASK) {
+-		DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
+-			   info->invflags, EBT_VLAN_MASK);
++		DEBUG_MSG("inversion flags %2X is out of mask (%2X)\n",
++			  info->invflags, EBT_VLAN_MASK);
+ 		return -EINVAL;
+ 	}
+ 
+ 	/*
+ 	 * Reserved VLAN ID (VID) values
+ 	 * -----------------------------
+-	 * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
+-	 * no VLAN identifier is present in the frame. This VID value shall not be
+-	 * configured as a PVID, configured in any Filtering Database entry, or used in any
+-	 * Management operation.
+-	 * 
+-	 * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
+-	 * Port. The PVID value can be changed by management on a per-Port basis.
+-	 * 
+-	 * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
+-	 * PVID or transmitted in a tag header.
+-	 * 
+-	 * The remaining values of VID are available for general use as VLAN identifiers.
+-	 * A Bridge may implement the ability to support less than the full range of VID values; 
+-	 * i.e., for a given implementation,
+-	 * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
+-	 * All implementations shall support the use of all VID values in the range 0 through their defined maximum
+-	 * VID, N.
+-	 * 
+-	 * For Linux, N = 4094.
++	 * 0 - The null VLAN ID. 
++	 * 1 - The default Port VID (PVID)
++	 * 0x0FFF - Reserved for implementation use. 
++	 * if_vlan.h: VLAN_GROUP_ARRAY_LEN 4096.
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
++	if (GET_BITMASK(EBT_VLAN_ID)) {	/* when vlan-id param was spec-ed */
+ 		if (!!info->id) {	/* if id!=0 => check vid range */
+-			if (info->id > 4094) {	/* check if id > than (0x0FFE) */
++			if (info->id > VLAN_GROUP_ARRAY_LEN) {
+ 				DEBUG_MSG
+-				    ("vlan id %d is out of range (1-4094)\n",
++				    ("id %d is out of range (1-4096)\n",
+ 				     info->id);
+ 				return -EINVAL;
+ 			}
+ 			/*
+ 			 * Note: This is valid VLAN-tagged frame point.
+-			 * Any value of user_priority are acceptable, but could be ignored
+-			 * according to 802.1Q Std.
+-			 */
+-		} else {
+-			/*
+-			 * if id=0 (null VLAN ID)  => Check for user_priority range 
+-			 */
+-			if (GET_BITMASK (EBT_VLAN_PRIO)) {
+-				if ((unsigned char) info->prio > 7) {
+-					DEBUG_MSG
+-					    ("prio %d is out of range (0-7)\n",
+-					     info->prio);
+-					return -EINVAL;
+-				}
+-			}
+-			/*
+-			 * Note2: This is valid priority-tagged frame point
+-			 * with null VID field.
++			 * Any value of user_priority are acceptable, 
++			 * but should be ignored according to 802.1Q Std.
++			 * So we just drop the prio flag. 
+ 			 */
++			info->bitmask &= ~EBT_VLAN_PRIO;
+ 		}
+-	} else {		/* VLAN Id not set */
+-		if (GET_BITMASK (EBT_VLAN_PRIO)) {	/* But user_priority is set - abnormal! */
+-			info->id = 0;	/* Set null VID (case for Priority-tagged frames) */
+-			SET_BITMASK (EBT_VLAN_ID);	/* and set id flag */
++		/*
++		 * Else, id=0 (null VLAN ID)  => user_priority range (any?)
++		 */
++	}
++
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if ((unsigned char) info->prio > 7) {
++			DEBUG_MSG
++			    ("prio %d is out of range (0-7)\n",
++			     info->prio);
++			return -EINVAL;
+ 		}
+ 	}
+ 	/*
+-	 * Check for encapsulated proto range - it is possible to be any value for u_short range.
+-	 * When relaying a tagged frame between 802.3/Ethernet MACs, 
+-	 * a Bridge may adjust the padding field such that
+-	 * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++	 * Check for encapsulated proto range - it is possible to be 
++	 * any value for u_short range.
+ 	 * if_ether.h:  ETH_ZLEN        60   -  Min. octets in frame sans FCS
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
+-		if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
+ 			DEBUG_MSG
+-			    ("encap packet length %d is less than minimal %d\n",
+-			     ntohs (info->encap), ETH_ZLEN);
++			    ("encap frame length %d is less than minimal\n",
++			     ntohs(info->encap));
+ 			return -EINVAL;
+ 		}
+ 	}
+ 
+-	/*
+-	 * Otherwise is all correct 
+-	 */
+-	DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
+-		   tablename, hooknr);
+ 	return 0;
+ }
+ 
+@@ -293,26 +236,24 @@
+ 
+ /*
+  * Module initialization function.
+- * Called when module is loaded to kernelspace
+  */
+-static int __init init (void)
++static int __init init(void)
+ {
+-	DEBUG_MSG ("ebtables 802.1Q extension module v"
+-		   MODULE_VERSION "\n");
+-	DEBUG_MSG ("module debug=%d\n", !!debug);
+-	return ebt_register_match (&filter_vlan);
++	DEBUG_MSG("ebtables 802.1Q extension module v"
++		  MODULE_VERSION "\n");
++	DEBUG_MSG("module debug=%d\n", !!debug);
++	return ebt_register_match(&filter_vlan);
+ }
+ 
+ /*
+  * Module "finalization" function
+- * Called when download module from kernelspace
+  */
+-static void __exit fini (void)
++static void __exit fini(void)
+ {
+-	ebt_unregister_match (&filter_vlan);
++	ebt_unregister_match(&filter_vlan);
+ }
+ 
+-module_init (init);
+-module_exit (fini);
++module_init(init);
++module_exit(fini);
+ 
+ EXPORT_NO_SYMBOLS;
+--- linux-2.4.20-pre7-ebt/net/bridge/netfilter/ebtables.c	Sat Dec  7 14:55:44 2002
++++ linux-2.4.20-ebt/net/bridge/netfilter/ebtables.c	Sat Dec  7 14:51:44 2002
+@@ -183,7 +183,7 @@
+ 	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+ 	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+ 	// base for chain jumps
+-	base = (char *)chaininfo;
++	base = private->entries;
+ 	i = 0;
+ 	while (i < nentries) {
+ 		if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
diff --git a/kernel/scripts/CopyRep b/kernel/scripts/CopyRep
new file mode 100755
index 0000000..4f172cb
--- /dev/null
+++ b/kernel/scripts/CopyRep
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Copies the repository files to the working kernel directory
+# This is used on a freshly untarred kernel
+
+# 23 August 2002 - permute br-nf-bds and ebtables copy order
+
+export FROM=/ebtables-cvs/ebtables2/
+export TO=/usr/src/linux-2.4.20-pre5
+
+mkdir -p $TO/net/bridge/netfilter
+mkdir -p $TO/include/linux/netfilter_bridge
+cp -r -f $FROM/kernel/linux/* $TO/
+cp -r -f $FROM/br-nf-bds/linux/* $TO/
+
+/bin/rm -r -f $TO/CVS
+/bin/rm -r -f $TO/include/CVS
+/bin/rm -r -f $TO/include/linux/CVS
+/bin/rm -r -f $TO/include/linux/netfilter_bridge/CVS
+/bin/rm -r -f $TO/net/CVS
+/bin/rm -r -f $TO/net/bridge/CVS
+/bin/rm -r -f $TO/net/bridge/netfilter/CVS
+/bin/rm -r -f $TO/core/CVS
+/bin/rm -r -f $TO/ipv4/CVS
+
diff --git a/kernel/scripts/CopyRep2.5 b/kernel/scripts/CopyRep2.5
new file mode 100755
index 0000000..aa158bf
--- /dev/null
+++ b/kernel/scripts/CopyRep2.5
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copies the repository files to the working kernel directory
+# This is used on a freshly untarred kernel
+
+# 23 August 2002 - this is for the files vs kernel 2.5
+
+# Change these 2 to the appropriate directories
+# FROM points to the CVS base of ebtables
+# TO points to the base of the kernel source tree where we want the files
+export FROM=/ebtables-cvs/ebtables2/
+export TO=/usr/src/linux-2.5.37
+
+mkdir -p $TO/net/bridge/netfilter
+mkdir -p $TO/include/linux/netfilter_bridge
+
+# the non-ebtables specific files shouldn't change often
+# /bin/cp -r -f $FROM/kernel/linux2.5/* $TO/
+/bin/cp -r -f $FROM/kernel/linux2.5/net/bridge/netfilter/* $TO/net/bridge/netfilter/
+/bin/cp -r -f $FROM/kernel/linux2.5/include/linux/netfilter_bridge/* $TO/include/linux/netfilter_bridge/
+/bin/rm -r -f $TO/net/bridge/netfilter/CVS
+/bin/rm -r -f $TO/include/linux/netfilter_bridge/CVS
+
+/bin/cp -r -f $FROM/br-nf-bds/linux2.5/* $TO/
+/bin/rm -r -f $TO/CVS
+/bin/rm -r -f $TO/include/CVS
+/bin/rm -r -f $TO/include/linux/CVS
+/bin/rm -r -f $TO/net/CVS
+/bin/rm -r -f $TO/net/bridge/CVS
+/bin/rm -r -f $TO/core/CVS
+/bin/rm -r -f $TO/ipv4/CVS
+
+
diff --git a/kernel/scripts/Makediff b/kernel/scripts/Makediff
new file mode 100755
index 0000000..7e22175
--- /dev/null
+++ b/kernel/scripts/Makediff
@@ -0,0 +1,100 @@
+#!/bin/bash
+# Makes a base diff for the ebtables kernel code
+
+# 6 June 2002: added ebt_snat.c, ebt_dnat.c files, removed ebt_nat.c file
+# 31 July 2002: added mark match/target, bridge/br.c, bridge/Makefile;
+#               deleted netsyms
+# 20 August 2002: deleted br_db
+# 18 September 2002: added br_forward.c
+# 3 September 2003: this file is now used for the ebt-brnf patch
+# 6 January 2004: add /include/linux/sysctl.h
+# 18 April 2004: added ipt_REJECT.c
+# 25 August 2004: added netfilter_ipv4.h
+# 25 November 2004: added ebt_ulog.h and ebt_ulog.c
+# 13 March 2005: added ip_tables.h, arp_tables.h and ip6_tables.h
+# 15 September 2005: added br_stp_bpdu.c
+# 9 March 2010: added include/net/ip.h, net/ipv4/netfilter/ip_conntrack_core.c
+
+export FROM=linux-2.4.37.9
+export TO=linux-2.4.37.9-ebt-brnf
+export FILE=ebtables-brnf-13_vs_2.4.37.9.diff
+
+diff -urNp --exclude TAGS $FROM/net/bridge/br_private.h $TO/net/bridge/br_private.h > $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/if_bridge.h $TO/include/linux/if_bridge.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/net/ip.h $TO/include/net/ip.h >> $FILE
+diff -urNp --exclude TAGS $FROM/net/core/dev.c $TO/net/core/dev.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/br_input.c $TO/net/bridge/br_input.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/br_stp_bpdu.c $TO/net/bridge/br_stp_bpdu.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/br_forward.c $TO/net/bridge/br_forward.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/br.c $TO/net/bridge/br.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/Makefile $TO/net/bridge/Makefile >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge.h $TO/include/linux/netfilter_bridge.h >> $FILE
+
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_ipv4/ip_tables.h $TO/include/linux/netfilter_ipv4/ip_tables.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_ipv6/ip6_tables.h $TO/include/linux/netfilter_ipv6/ip6_tables.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_arp/arp_tables.h $TO/include/linux/netfilter_arp/arp_tables.h >> $FILE
+
+# only needed for 2.4.x
+diff -urNp --exclude TAGS $FROM/net/Makefile $TO/net/Makefile >> $FILE
+diff -urNp --exclude TAGS $FROM/net/Config.in $TO/net/Config.in >> $FILE
+
+diff -urN /dev/null $TO/net/bridge/netfilter/Makefile >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/Config.in >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebtable_filter.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebtable_nat.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebtable_broute.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_among.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_limit.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_arpreply.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_802_3.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_mark.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_mark_m.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_pkttype.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_stp.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_redirect.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_arp.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_ip.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_vlan.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_log.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_ulog.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_snat.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebt_dnat.c >> $FILE
+diff -urN /dev/null $TO/net/bridge/netfilter/ebtables.c >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebtables.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_among.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_limit.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_arpreply.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_802_3.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_arp.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_ip.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_pkttype.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_stp.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_vlan.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_log.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_ulog.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_nat.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_redirect.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_mark_m.h >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_bridge/ebt_mark_t.h >> $FILE
+
+diff -urNp $FROM/include/linux/netfilter.h $TO/include/linux/netfilter.h >> $FILE
+diff -urNp $FROM/include/linux/netfilter_ipv4.h $TO/include/linux/netfilter_ipv4.h >> $FILE
+diff -urNp $FROM/include/linux/netfilter_ipv6.h $TO/include/linux/netfilter_ipv6.h >> $FILE
+diff -urNp $FROM/include/linux/skbuff.h $TO/include/linux/skbuff.h >> $FILE
+diff -urNp $FROM/net/core/netfilter.c $TO/net/core/netfilter.c >> $FILE
+diff -urNp $FROM/net/core/skbuff.c $TO/net/core/skbuff.c >> $FILE
+diff -urNp $FROM/net/ipv4/netfilter/ip_tables.c $TO/net/ipv4/netfilter/ip_tables.c >> $FILE
+diff -urNp $FROM/net/ipv4/netfilter/ip_conntrack_core.c $TO/net/ipv4/netfilter/ip_conntrack_core.c >> $FILE
+diff -urNp $FROM/net/ipv4/ip_output.c $TO/net/ipv4/ip_output.c >> $FILE
+diff -urNp $FROM/net/ipv4/netfilter/ipt_LOG.c $TO/net/ipv4/netfilter/ipt_LOG.c >> $FILE
+diff -urNp $FROM/net/ipv4/netfilter/Makefile $TO/net/ipv4/netfilter/Makefile >> $FILE
+diff -urNp $FROM/net/ipv4/netfilter/Config.in $TO/net/ipv4/netfilter/Config.in >> $FILE
+diff -urN /dev/null $TO/net/bridge/br_netfilter.c >> $FILE
+
+diff -urN /dev/null $TO/net/ipv4/netfilter/ipt_physdev.c >> $FILE
+diff -urN /dev/null $TO/include/linux/netfilter_ipv4/ipt_physdev.h >> $FILE
+
+diff -urNp $FROM/net/8021q/vlan_dev.c $TO/net/8021q/vlan_dev.c >> $FILE
+diff -urNp $FROM/include/linux/sysctl.h $TO/include/linux/sysctl.h >> $FILE
+
+diff -urNp $FROM/net/ipv4/netfilter/ipt_REJECT.c $TO/net/ipv4/netfilter/ipt_REJECT.c >> $FILE
diff --git a/kernel/scripts/Makeincrdiff b/kernel/scripts/Makeincrdiff
new file mode 100755
index 0000000..3b9b590
--- /dev/null
+++ b/kernel/scripts/Makeincrdiff
@@ -0,0 +1,52 @@
+#!/bin/bash
+# used for making the incremental kernel diffs
+# 31 July 2002: added mark match/target, bridge/br.c, bridge/Makefile;
+#               deleted netsyms
+# 10 September 2002: added br_forward.c
+
+export FROM=linux-2.4.20-pre5
+export TO=linux-2.4.20-pre5-ebtables
+export FILE=ebtables-v2.0_vs_2.4.20-pre5.001.diff
+
+# brouter support
+
+diff -urN $FROM/net/bridge/br_private.h $TO/net/bridge/br_private.h > $FILE
+diff -urN $FROM/include/linux/if_bridge.h $TO/include/linux/if_bridge.h >> $FILE
+diff -urN $FROM/net/core/dev.c $TO/net/core/dev.c >> $FILE
+diff -urN $FROM/net/bridge/br_input.c $TO/net/bridge/br_input.c >> $FILE
+diff -urN $FROM/net/bridge/br_forward.c $TO/net/bridge/br_forward.c >> $FILE
+diff -urN $FROM/net/bridge/br.c $TO/net/bridge/br.c >> $FILE
+diff -urN $FROM/net/bridge/Makefile $TO/net/bridge/Makefile >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge.h $TO/include/linux/netfilter_bridge.h >> $FILE
+
+# getting ebtables compilable
+
+diff -urN $FROM/net/Makefile $TO/net/Makefile >> $FILE
+diff -urN $FROM/net/Config.in $TO/net/Config.in >> $FILE
+
+# newly created files
+
+diff -urN $FROM/net/bridge/netfilter/Makefile $TO/net/bridge/netfilter/Makefile >> $FILE
+diff -urN $FROM/net/bridge/netfilter/Config.in $TO/net/bridge/netfilter/Config.in >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebtable_filter.c $TO/net/bridge/netfilter/ebtable_filter.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebtable_nat.c $TO/net/bridge/netfilter/ebtable_nat.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebtable_broute.c $TO/net/bridge/netfilter/ebtable_broute.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_redirect.c $TO/net/bridge/netfilter/ebt_redirect.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_arp.c $TO/net/bridge/netfilter/ebt_arp.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_ip.c $TO/net/bridge/netfilter/ebt_ip.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_vlan.c $TO/net/bridge/netfilter/ebt_vlan.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_log.c $TO/net/bridge/netfilter/ebt_log.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_mark.c $TO/net/bridge/netfilter/ebt_mark.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_mark_m.c $TO/net/bridge/netfilter/ebt_mark_m.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_snat.c $TO/net/bridge/netfilter/ebt_snat.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebt_dnat.c $TO/net/bridge/netfilter/ebt_dnat.c >> $FILE
+diff -urN $FROM/net/bridge/netfilter/ebtables.c $TO/net/bridge/netfilter/ebtables.c >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebtables.h $TO/include/linux/netfilter_bridge/ebtables.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_arp.h $TO/include/linux/netfilter_bridge/ebt_arp.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_ip.h $TO/include/linux/netfilter_bridge/ebt_ip.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_vlan.h $TO/include/linux/netfilter_bridge/ebt_vlan.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_log.h $TO/include/linux/netfilter_bridge/ebt_log.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_mark_t.h $TO/include/linux/netfilter_bridge/ebt_mark_t.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_mark_m.h $TO/include/linux/netfilter_bridge/ebt_mark_m.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_nat.h $TO/include/linux/netfilter_bridge/ebt_nat.h >> $FILE
+diff -urN $FROM/include/linux/netfilter_bridge/ebt_redirect.h $TO/include/linux/netfilter_bridge/ebt_redirect.h >> $FILE
diff --git a/kernel/scripts/Makeincrdiff2.5 b/kernel/scripts/Makeincrdiff2.5
new file mode 100755
index 0000000..7e993a6
--- /dev/null
+++ b/kernel/scripts/Makeincrdiff2.5
@@ -0,0 +1,37 @@
+#!/bin/bash
+# used for making the incremental kernel 2.5.x diffs
+
+#export FROM=/ebtables2/kernel/linux/
+export FROM=linux-2.6.0-test1
+export TO=linux-2.6.0-test1-keep
+export FILE=file.diff
+
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/Makefile $TO/net/bridge/netfilter/Makefile >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebtable_filter.c $TO/net/bridge/netfilter/ebtable_filter.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebtable_nat.c $TO/net/bridge/netfilter/ebtable_nat.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebtable_broute.c $TO/net/bridge/netfilter/ebtable_broute.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_redirect.c $TO/net/bridge/netfilter/ebt_redirect.c >> $FILE
+#diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_802_3.c $TO/net/bridge/netfilter/ebt_802_3.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_arp.c $TO/net/bridge/netfilter/ebt_arp.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_ip.c $TO/net/bridge/netfilter/ebt_ip.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_stp.c $TO/net/bridge/netfilter/ebt_stp.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_pkttype.c $TO/net/bridge/netfilter/ebt_pkttype.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_vlan.c $TO/net/bridge/netfilter/ebt_vlan.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_log.c $TO/net/bridge/netfilter/ebt_log.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_mark.c $TO/net/bridge/netfilter/ebt_mark.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_mark_m.c $TO/net/bridge/netfilter/ebt_mark_m.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_snat.c $TO/net/bridge/netfilter/ebt_snat.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebt_dnat.c $TO/net/bridge/netfilter/ebt_dnat.c >> $FILE
+diff -urNp --exclude TAGS $FROM/net/bridge/netfilter/ebtables.c $TO/net/bridge/netfilter/ebtables.c >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebtables.h $TO/include/linux/netfilter_bridge/ebtables.h >> $FILE
+#diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_802_3.h $TO/include/linux/netfilter_bridge/ebt_802_3.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_arp.h $TO/include/linux/netfilter_bridge/ebt_arp.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_ip.h $TO/include/linux/netfilter_bridge/ebt_ip.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_stp.h $TO/include/linux/netfilter_bridge/ebt_stp.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_vlan.h $TO/include/linux/netfilter_bridge/ebt_vlan.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_log.h $TO/include/linux/netfilter_bridge/ebt_log.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_pkttype.h $TO/include/linux/netfilter_bridge/ebt_pkttype.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_mark_t.h $TO/include/linux/netfilter_bridge/ebt_mark_t.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_mark_m.h $TO/include/linux/netfilter_bridge/ebt_mark_m.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_nat.h $TO/include/linux/netfilter_bridge/ebt_nat.h >> $FILE
+diff -urNp --exclude TAGS $FROM/include/linux/netfilter_bridge/ebt_redirect.h $TO/include/linux/netfilter_bridge/ebt_redirect.h >> $FILE
diff --git a/userspace/README b/userspace/README
new file mode 100644
index 0000000..c4c80d0
--- /dev/null
+++ b/userspace/README
@@ -0,0 +1,13 @@
+---
+Tag naming for the userspace code:
+All files in ebtables2 are tagged like this example:
+ebtables-2-0-pre6
+June 1, 2002
+---
+Release candidats are tagged like this:
+ebtables-2-0-rc1
+July 31, 2002
+---
+Stable versions are tagged like this:
+ebtables-2-0
+September 19, 2002
diff --git a/userspace/arptables/Makefile b/userspace/arptables/Makefile
new file mode 100644
index 0000000..f413b13
--- /dev/null
+++ b/userspace/arptables/Makefile
@@ -0,0 +1,76 @@
+ARPTABLES_VERSION:=0.0.3-4
+
+KERNEL_DIR:=./
+# default paths
+PREFIX:=/usr/local
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INITDIR:=/etc/rc.d/init.d
+SYSCONFIGDIR:=/etc/sysconfig
+DESTDIR:=
+
+COPT_FLAGS:=-O2
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include/ -Iinclude/ -DARPTABLES_VERSION=\"$(ARPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DARPTC_DEBUG
+
+ifndef ARPT_LIBDIR
+ARPT_LIBDIR:=$(LIBDIR)/arptables
+endif
+
+include extensions/Makefile
+
+all: arptables
+
+arptables.o: arptables.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+arptables-standalone.o: arptables-standalone.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+libarptc/libarptc.o: libarptc/libarptc.c libarptc/libarptc_incl.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+arptables: arptables-standalone.o arptables.o libarptc/libarptc.o $(EXT_OBJS)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
+
+$(DESTDIR)$(MANDIR)/man8/arptables.8: arptables.8
+	mkdir -p $(@D)
+	install -m 0644 $< $@
+
+$(DESTDIR)$(BINDIR)/arptables: arptables
+	mkdir -p $(DESTDIR)$(BINDIR)
+	install -m 0755 $< $@
+
+tmp1:=$(shell printf $(BINDIR) | sed 's/\//\\\//g')
+tmp2:=$(shell printf $(SYSCONFIGDIR) | sed 's/\//\\\//g')
+.PHONY: scripts
+scripts: arptables-save arptables-restore arptables.sysv
+	cat arptables-save | sed 's/__EXEC_PATH__/$(tmp1)/g' > arptables-save_
+	install -m 0755 arptables-save_ $(DESTDIR)$(BINDIR)/arptables-save
+	cat arptables-restore | sed 's/__EXEC_PATH__/$(tmp1)/g' > arptables-restore_
+	install -m 0755 arptables-restore_ $(DESTDIR)$(BINDIR)/arptables-restore
+	cat arptables.sysv | sed 's/__EXEC_PATH__/$(tmp1)/g' | sed 's/__SYSCONFIG__/$(tmp2)/g' > arptables.sysv_
+	if [ "$(DESTDIR)" != "" ]; then mkdir -p $(DESTDIR)$(INITDIR); fi
+	if test -d $(DESTDIR)$(INITDIR); then install -m 0755 arptables.sysv_ $(DESTDIR)$(INITDIR)/arptables; fi
+	rm -f arptables-save_ arptables-restore_ arptables.sysv_
+
+.PHONY: install
+install: $(DESTDIR)$(MANDIR)/man8/arptables.8 $(DESTDIR)$(BINDIR)/arptables scripts
+
+.PHONY: clean
+clean:
+	rm -f arptables
+	rm -f *.o *~
+	rm -f extensions/*.o extensions/*~
+	rm -f libarptc/*.o libarptc/*~
+	rm -f include/*~ include/libarptc/*~
+
+DIR:=arptables-v$(ARPTABLES_VERSION)
+CVSDIRS:=CVS extensions/CVS libarptc/CVS include/CVS include/libarptc/CVS include/linux/CVS include/linux/netfilter_arp/CVS
+# This is used to make a new userspace release
+.PHONY: release
+release:
+	rm -rf $(CVSDIRS)
+	make clean
+	cd ..;find $(DIR) -exec touch {} \;;find $(DIR) -exec chmod o-r,g-r,o-w,g-w,o-x,g-x {} \;;tar -pc $(DIR) | gzip >$(DIR).tar.gz
+
diff --git a/userspace/arptables/arptables-restore b/userspace/arptables/arptables-restore
new file mode 100644
index 0000000..c509702
--- /dev/null
+++ b/userspace/arptables/arptables-restore
@@ -0,0 +1,67 @@
+#!/usr/bin/perl -w
+# A script that imports text arptables rules. Similar to iptables-restore.
+
+use strict;
+my $tool = "__EXEC_PATH__/arptables";
+my $table;
+my $rc;
+my $line;
+
+# ==============================
+# clear_arptables
+# - sets policy to accept
+# - flushes chains
+# - removes custom chains
+# ==============================
+sub clear_arptables {
+	$rc = `$tool -P INPUT ACCEPT`;
+	unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+	$rc = `$tool -P FORWARD ACCEPT`;
+	unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+	$rc = `$tool -P OUTPUT ACCEPT`;
+	unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+
+	$rc = `$tool -F`;
+	unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+
+	$rc = `$tool -L`;
+	unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+	
+	foreach $line (split("\n",$rc)) {
+		unless ($line =~ m/Chain\s(.*?)\s\(.*references\)/) { next; }
+		$rc = `$tool -X $1`;
+		unless($? == 0) { print "ERROR: $rc\n"; exit -1 };
+	}
+}
+# ==============================
+
+
+unless (-x $tool) { print "ERROR: $tool isn't executable\n"; exit -1; };
+&clear_arptables();
+
+$line = 0;
+while(<>) {
+    $line++;
+    if(m/^#/) { next; };
+    if(m/^$/) { next; };
+
+    if(m/^\*(.*)/) {
+        $table = $1;
+        next;
+    }
+
+    # Process a chain directive
+    if(m/^\:(.*?)\s(.*)/) {
+	# is it a user or a built in chain ?
+	if ("$2" eq "-") { 
+        	$rc = `$tool -t $table -N $1`;
+	        unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1};
+		next; 
+	}
+        $rc = `$tool -t $table -P $1 $2`;
+        unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1};
+        next;
+    }
+    $rc = `$tool -t $table $_`;
+    unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1};
+}
diff --git a/userspace/arptables/arptables-save b/userspace/arptables/arptables-save
new file mode 100644
index 0000000..f434db3
--- /dev/null
+++ b/userspace/arptables/arptables-save
@@ -0,0 +1,50 @@
+#!/usr/bin/perl -w
+#
+#
+# A script that generates text output of the arptables rules.
+# Similar to iptables-save.
+
+use strict;
+my $table;
+my $tool = "__EXEC_PATH__/arptables";
+
+# ========================================================
+# Process filter table
+# ========================================================
+sub process_table {
+    my $chain = "";
+    my $rules = "";
+    my $chains = "";
+    my $custom_chains = "";
+    my $line = "";
+
+    foreach $line (split("\n",$_[0])) {
+        if ($line =~ m/Chain\s(.*?)\s\(policy\s(.*?)\)/) {
+            $chains = $chains . ":$1 $2\n";
+            $chain = $1;
+            next;
+        }
+        if ($line =~ m/Chain\s(.*?)\s\(/) {
+            $custom_chains = $custom_chains . ":$1 -\n";
+            $chain = $1;
+            next;
+        }
+        if ($line =~ m/^$/) {
+            next;
+        }
+        $rules = $rules . "-A $chain $line\n";
+    }
+
+    print "*filter\n";
+    print $chains;
+    print $custom_chains;
+    print $rules;
+    print "\n";
+}
+# ========================================================
+
+unless (-x "$tool") { print "ERROR: Tool $tool isn't executable"; exit -1; };
+$table =`$tool -t filter -L -n`;
+unless ($? == 0) { print $table; exit -1 };
+&process_table($table);
+
diff --git a/userspace/arptables/arptables-standalone.c b/userspace/arptables/arptables-standalone.c
new file mode 100644
index 0000000..3764996
--- /dev/null
+++ b/userspace/arptables/arptables-standalone.c
@@ -0,0 +1,60 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * 		    Marc Boucher <marc+nf@mbsi.ca>
+ * 		    James Morris <jmorris@intercode.com.au>
+ * 		    Harald Welte <laforge@gnumonks.org>
+ * 		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *	arptables -- IP firewall administration for kernels with
+ *	firewall table (aimed for the 2.3 kernels)
+ *
+ *	See the accompanying manual page arptables(8) for information
+ *	about proper usage of this program.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <arptables.h>
+
+int
+main(int argc, char *argv[])
+{
+	int ret;
+	char *table = "filter";
+	arptc_handle_t handle = NULL;
+
+	program_name = "arptables";
+
+/*	init_extensions();
+*/
+	ret = do_command(argc, argv, &table, &handle);
+	if (ret)
+		ret = arptc_commit(&handle);
+
+	if (!ret)
+		fprintf(stderr, "arptables: %s\n",
+			arptc_strerror(errno));
+
+	exit(!ret);
+}
diff --git a/userspace/arptables/arptables.8 b/userspace/arptables/arptables.8
new file mode 100644
index 0000000..d3059d9
--- /dev/null
+++ b/userspace/arptables/arptables.8
@@ -0,0 +1,315 @@
+.TH ARPTABLES 8  "August 2007"
+.\"
+.\" Man page originally written by Jochen Friedrich <jochen@scram.de>,
+.\" maintained by Bart De Schuymer.
+.\" It is based on the iptables man page.
+.\"
+.\" Iptables page by Herve Eychenne March 2000.
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+arptables \- ARP table administration
+.SH SYNOPSIS
+.BR "arptables " [ "-t table" ] " -" [ AD ] " chain rule-specification " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ RI ] " chain rulenum rule-specification " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -D chain rulenum " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ "LFZ" ] " " [ chain ] " " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ "NX" ] " chain"
+.br
+.BR "arptables " [ "-t table" ] " -E old-chain-name new-chain-name"
+.br
+.BR "arptables " [ "-t table" ] " -P chain target " [ options ]
+.SH DESCRIPTION
+.B arptables
+is a user space tool, it is used to set up and maintain the
+tables of ARP rules in the Linux kernel. These rules inspect
+the ARP frames which they see.
+.B arptables
+is analogous to the
+.B iptables
+user space tool, but
+.B arptables
+is less complicated.
+
+.SS CHAINS
+The kernel table is used to divide functionality into
+different sets of rules. Each set of rules is called a chain.
+Each chain is an ordered list of rules that can match ARP frames. If a
+rule matches an ARP frame, then a processing specification tells
+what to do with that matching frame. The processing specification is
+called a 'target'. However, if the frame does not match the current
+rule in the chain, then the next rule in the chain is examined and so forth.
+The user can create new (user-defined) chains which can be used as the 'target' of a rule.
+
+.SS TARGETS
+A firewall rule specifies criteria for an ARP frame and a frame
+processing specification called a target.  When a frame matches a rule,
+then the next action performed by the kernel is specified by the target.
+The target can be one of these values:
+.IR ACCEPT ,
+.IR DROP ,
+.IR CONTINUE ,
+.IR RETURN ,
+an 'extension' (see below) or a user-defined chain.
+.PP
+.I ACCEPT
+means to let the frame through.
+.I DROP
+means the frame has to be dropped.
+.I CONTINUE
+means the next rule has to be checked. This can be handy to know how many
+frames pass a certain point in the chain or to log those frames.
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.
+For the extension targets please see the
+.B "TARGET EXTENSIONS"
+section of this man page.
+.SS TABLES
+There is only one ARP table in the Linux
+kernel.  The table is
+.BR filter.
+You can drop the '-t filter' argument to the arptables command.
+The -t argument must be the
+first argument on the arptables command line, if used.
+.TP
+.B "-t, --table"
+.br
+.BR filter ,
+is the only table and contains two (Linux kernels 2.4.X) or three (Linux kernels 2.6.0 and later) built-in chains:
+.B INPUT 
+(for frames destined for the host), 
+.B OUTPUT 
+(for locally-generated frames) and
+.B FORWARD
+(for frames being forwarded by the bridge code). The
+.B FORWARD
+chain doesn't exist in Linux 2.4.X kernels.
+.br
+.br
+.SH ARPTABLES COMMAND LINE ARGUMENTS
+After the initial arptables command line argument, the remaining
+arguments can be divided into several different groups.  These groups
+are commands, miscellaneous commands, rule-specifications, match-extensions,
+and watcher-extensions.
+.SS COMMANDS
+The arptables command arguments specify the actions to perform on the table
+defined with the -t argument.  If you do not use the -t argument to name
+a table, the commands apply to the default filter table.
+With the exception of the
+.B "-Z"
+command, only one command may be used on the command line at a time.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to delete, syntax: start_nr[:end_nr]. Using negative numbers is allowed, for more
+details about using negative numbers, see the -I command. The second usage is by
+specifying the complete rule as it would have been specified when it was added.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number.
+If the current number of rules equals N, then the specified number can be
+between -N and N+1. For a positive number i, it holds that i and i-N-1 specify the
+same place in the chain where the rule should be inserted. The number 0 specifies
+the place past the last rule in the chain and using this number is therefore
+equivalent with using the -A command.
+.TP
+.B "-R, --replace"
+Replaces the specified rule into the selected chain at the specified rule number.
+If the current number of rules equals N, then the specified number can be
+between 1 and N. i specifies the place in the chain where the rule should be replaced.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy can be
+.BR ACCEPT ", " DROP " or " RETURN .
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, then every chain will be
+flushed. Flushing the chain does not change the policy of the
+chain, however.
+.TP
+.B "-Z, --zero"
+Set the counters of the selected chain to zero. If no chain is selected, all the counters
+are set to zero. The
+.B "-Z"
+command can be used in conjunction with the 
+.B "-L"
+command.
+When both the
+.B "-Z"
+and
+.B "-L"
+commands are used together in this way, the rule counters are printed on the screen
+before they are set to zero.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain with the given name. The number of
+user-defined chains is unlimited. A user-defined chain name has maximum
+length of 31 characters.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no remaining references
+to the specified chain, otherwise
+.B arptables
+will refuse to delete it. If no chain is specified, all user-defined
+chains that aren't referenced will be removed.
+.TP
+.B "-E, --rename-chain"
+Rename the specified chain to a new name.  Besides renaming a user-defined
+chain, you may rename a standard chain name to a name that suits your
+taste. For example, if you like PREBRIDGING more than PREROUTING,
+then you can use the -E command to rename the PREROUTING chain. If you do
+rename one of the standard
+.B arptables
+chain names, please be sure to mention
+this fact should you post a question on the
+.B arptables
+mailing lists.
+It would be wise to use the standard name in your post. Renaming a standard
+.B arptables
+chain in this fashion has no effect on the structure or function
+of the
+.B arptables
+kernel table.
+
+.SS MISCELLANOUS COMMANDS
+.TP
+.B "-V, --version"
+Show the version of the arptables userspace program.
+.TP
+.B "-h, --help"
+Give a brief description of the command syntax.
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+a target extension (see
+.BR "TARGET EXTENSIONS" ")"
+or a user-defined chain name.
+
+.SS RULE-SPECIFICATIONS
+The following command line arguments make up a rule specification (as used 
+in the add and delete commands). A "!" option before the specification 
+inverts the test for that specification. Apart from these standard rule 
+specifications there are some other command line arguments of interest.
+.TP
+.BR "-s, --source-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+The Source IP specification.
+.TP 
+.BR "-d, --destination-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+The Destination IP specification.
+.TP 
+.BR "--source-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+The source mac address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons.
+.TP
+.BR "--destination-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination mac address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons.
+.TP 
+.BR "-i, --in-interface " "[!] \fIname\fP"
+The interface via which a frame is received (for the
+.BR INPUT " and " FORWARD
+chains). The flag
+.B --in-if
+is an alias for this option.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface via which a frame is going to be sent (for the
+.BR OUTPUT " and " FORWARD
+chains). The flag
+.B --out-if
+is an alias for this option.
+.TP
+.BR "-l, --h-length " "\fIlength\fP[/\fImask\fP]"
+The hardware length (nr of bytes)
+.TP
+.BR "--opcode " "\fIcode\fP[/\fImask\fP]
+The operation code (2 bytes). Available values are:
+.BR 1 = Request
+.BR 2 = Reply
+.BR 3 = Request_Reverse
+.BR 4 = Reply_Reverse
+.BR 5 = DRARP_Request
+.BR 6 = DRARP_Reply
+.BR 7 = DRARP_Error
+.BR 8 = InARP_Request
+.BR 9 = ARP_NAK .
+.TP
+.BR "--h-type " "\fItype\fP[/\fImask\fP]"
+The hardware type (2 bytes, hexadecimal). Available values are:
+.BR 1 = Ethernet .
+.TP
+.BR "--proto-type " "\fItype\fP[/\fImask\fP]"
+The protocol type (2 bytes). Available values are:
+.BR 0x800 = IPv4 .
+
+.SS TARGET-EXTENSIONS
+.B arptables
+extensions are precompiled into the userspace tool. So there is no need
+to explicitly load them with a -m option like in
+.BR iptables .
+However, these
+extensions deal with functionality supported by supplemental kernel modules.
+.SS mangle
+.TP
+.BR "--mangle-ip-s IP address"
+Mangles Source IP Address to given value.
+.TP
+.BR "--mangle-ip-d IP address"
+Mangles Destination IP Address to given value.
+.TP
+.BR "--mangle-mac-s MAC address"
+Mangles Source MAC Address to given value.
+.TP
+.BR "--mangle-mac-d MAC address"
+Mangles Destination MAC Address to given value.
+.TP
+.BR "--mangle-target target "
+Target of ARP mangle operation
+.BR "" ( DROP ", " CONTINUE " or " ACCEPT " -- default is " ACCEPT ).
+.SS CLASSIFY
+This  module  allows you to set the skb->priority value (and thus clas-
+sify the packet into a specific CBQ class).
+
+.TP
+.BR "--set-class major:minor"
+
+Set the major and minor  class  value.  The  values  are  always
+interpreted as hexadecimal even if no 0x prefix is given.
+
+.SH MAILINGLISTS
+.BR "" "See " http://netfilter.org/mailinglists.html
+.SH SEE ALSO
+.BR iptables "(8), " ebtables "(8), " arp "(8), " rarp "(8), " ifconfig "(8), " route (8)
+.PP
+.BR "" "See " http://ebtables.sf.net
diff --git a/userspace/arptables/arptables.c b/userspace/arptables/arptables.c
new file mode 100644
index 0000000..2f048c7
--- /dev/null
+++ b/userspace/arptables/arptables.c
@@ -0,0 +1,2489 @@
+/* Code to take an arptables-style command line and do it. */
+
+/*
+ * arptables:
+ * Author: Bart De Schuymer <bdschuym@pandora.be>, but
+ * almost all code is from the iptables userspace program, which has main
+ * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+  Currently, only support for specifying hardware addresses for Ethernet
+  is available.
+  This tool is not luser-proof: you can specify an Ethernet source address
+  and set hardware length to something different than 6, f.e.
+*/
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <arptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef ARPT_LIB_DIR
+#define ARPT_LIB_DIR "/usr/local/lib/arptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC	0x0001
+#define FMT_NOCOUNTS	0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS	0x0008
+#define FMT_NOTABLE	0x0010
+#define FMT_NOTARGET	0x0020
+#define FMT_VIA		0x0040
+#define FMT_NONEWLINE	0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+			| FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (notab))
+
+
+#define CMD_NONE		0x0000U
+#define CMD_INSERT		0x0001U
+#define CMD_DELETE		0x0002U
+#define CMD_DELETE_NUM		0x0004U
+#define CMD_REPLACE		0x0008U
+#define CMD_APPEND		0x0010U
+#define CMD_LIST		0x0020U
+#define CMD_FLUSH		0x0040U
+#define CMD_ZERO		0x0080U
+#define CMD_NEW_CHAIN		0x0100U
+#define CMD_DELETE_CHAIN	0x0200U
+#define CMD_SET_POLICY		0x0400U
+#define CMD_CHECK		0x0800U
+#define CMD_RENAME_CHAIN	0x1000U
+#define NUMBER_OF_CMD	13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+				 'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE	0x00000U
+#define OPT_NUMERIC	0x00001U
+#define OPT_S_IP	0x00002U
+#define OPT_D_IP	0x00004U
+#define OPT_S_MAC	0x00008U
+#define OPT_D_MAC	0x00010U
+#define OPT_H_LENGTH	0x00020U
+#define OPT_P_LENGTH	0x00040U
+#define OPT_OPCODE	0x00080U
+#define OPT_H_TYPE	0x00100U
+#define OPT_P_TYPE	0x00200U
+#define OPT_JUMP	0x00400U
+#define OPT_VERBOSE	0x00800U
+#define OPT_VIANAMEIN	0x01000U
+#define OPT_VIANAMEOUT	0x02000U
+#define OPT_LINENUMBERS 0x04000U
+#define OPT_COUNTERS	0x08000U
+#define NUMBER_OF_OPT	16
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};
+
+static struct option original_opts[] = {
+	{ "append", 1, 0, 'A' },
+	{ "delete", 1, 0,  'D' },
+	{ "insert", 1, 0,  'I' },
+	{ "replace", 1, 0,  'R' },
+	{ "list", 2, 0,  'L' },
+	{ "flush", 2, 0,  'F' },
+	{ "zero", 2, 0,  'Z' },
+	{ "new-chain", 1, 0,  'N' },
+	{ "delete-chain", 2, 0,  'X' },
+	{ "rename-chain", 1, 0,  'E' },
+	{ "policy", 1, 0,  'P' },
+	{ "source-ip", 1, 0, 's' },
+	{ "destination-ip", 1, 0,  'd' },
+	{ "src-ip", 1, 0,  's' },
+	{ "dst-ip", 1, 0,  'd' },
+	{ "source-mac", 1, 0, 2},
+	{ "destination-mac", 1, 0, 3},
+	{ "src-mac", 1, 0, 2},
+	{ "dst-mac", 1, 0, 3},
+	{ "h-length", 1, 0,  'l' },
+	{ "p-length", 1, 0,  8 },
+	{ "opcode", 1, 0,  4 },
+	{ "h-type", 1, 0,  5 },
+	{ "proto-type", 1, 0,  6 },
+	{ "in-interface", 1, 0, 'i' },
+	{ "jump", 1, 0, 'j' },
+	{ "table", 1, 0, 't' },
+	{ "match", 1, 0, 'm' },
+	{ "numeric", 0, 0, 'n' },
+	{ "out-interface", 1, 0, 'o' },
+	{ "verbose", 0, 0, 'v' },
+	{ "exact", 0, 0, 'x' },
+	{ "version", 0, 0, 'V' },
+	{ "help", 2, 0, 'h' },
+	{ "line-numbers", 0, 0, '0' },
+	{ "modprobe", 1, 0, 'M' },
+	{ 0 }
+};
+
+int RUNTIME_NF_ARP_NUMHOOKS = 3;
+
+/*#ifndef __OPTIMIZE__
+struct arpt_entry_target *
+arpt_get_target(struct arpt_entry *e)
+{
+	return (void *)e + e->target_offset;
+}
+#endif*/
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
+/*INSERT*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DELETE*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*REPLACE*/   {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*APPEND*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*LIST*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*FLUSH*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*ZERO*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*CHECK*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
+/*RENAME*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ ARPT_INV_SRCIP,
+/* -d */ ARPT_INV_TGTIP,
+/* 2 */ ARPT_INV_SRCDEVADDR,
+/* 3 */ ARPT_INV_TGTDEVADDR,
+/* -l */ ARPT_INV_ARPHLN,
+/* 4 */ ARPT_INV_ARPOP,
+/* 5 */ ARPT_INV_ARPHRD,
+/* 6 */ ARPT_INV_ARPPRO,
+/* -j */ 0,
+/* -v */ 0,
+/* -i */ ARPT_INV_VIA_IN,
+/* -o */ ARPT_INV_VIA_OUT,
+/*--line*/ 0,
+/* -c */ 0,
+};
+
+const char *program_version = ARPTABLES_VERSION;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct arptables_match *arptables_matches = NULL;
+struct arptables_target *arptables_targets = NULL;
+
+/* Extra debugging from libarptc */
+extern void dump_entries(const arptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+	char *name;
+	u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+/***********************************************/
+/* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
+/***********************************************/
+
+unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
+unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+
+/* a few names */
+static char *opcodes[] =
+{
+	"Request",
+	"Reply",
+	"Request_Reverse",
+	"Reply_Reverse",
+	"DRARP_Request",
+	"DRARP_Reply",
+	"DRARP_Error",
+	"InARP_Request",
+	"ARP_NAK",
+};
+#define NUMOPCODES 9
+
+/*
+ * put the mac address into 6 (ETH_ALEN) bytes
+ */
+int getmac_and_mask(char *from, char *to, char *mask)
+{
+	char *p;
+	int i;
+	struct ether_addr *addr;
+
+	if (strcasecmp(from, "Unicast") == 0) {
+		memcpy(to, mac_type_unicast, ETH_ALEN);
+		memcpy(mask, msk_type_unicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Multicast") == 0) {
+		memcpy(to, mac_type_multicast, ETH_ALEN);
+		memcpy(mask, msk_type_multicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Broadcast") == 0) {
+		memcpy(to, mac_type_broadcast, ETH_ALEN);
+		memcpy(mask, msk_type_broadcast, ETH_ALEN);
+		return 0;
+	}
+	if ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		if (!(addr = ether_aton(p + 1)))
+			return -1;
+		memcpy(mask, addr, ETH_ALEN);
+	} else
+		memset(mask, 0xff, ETH_ALEN);
+	if (!(addr = ether_aton(from)))
+		return -1;
+	memcpy(to, addr, ETH_ALEN);
+	for (i = 0; i < ETH_ALEN; i++)
+		to[i] &= mask[i];
+	return 0;
+}
+
+int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask)
+{
+	char *p, *buffer;
+	int i;
+
+	if ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		i = strtol(p+1, &buffer, 10);
+		if (*buffer != '\0' || i < 0 || i > 255)
+			return -1;
+		*mask = (uint8_t)i;
+	} else
+		*mask = 255;
+	i = strtol(from, &buffer, 10);
+	if (*buffer != '\0' || i < 0 || i > 255)
+		return -1;
+	*to = (uint8_t)i;
+	return 0;
+}
+
+int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
+{
+	char *p, *buffer;
+	int i;
+
+	if ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		i = strtol(p+1, &buffer, base);
+		if (*buffer != '\0' || i < 0 || i > 65535)
+			return -1;
+		*mask = htons((uint16_t)i);
+	} else
+		*mask = 65535;
+	i = strtol(from, &buffer, base);
+	if (*buffer != '\0' || i < 0 || i > 65535)
+		return -1;
+	*to = htons((uint16_t)i);
+	return 0;
+}
+
+void print_mac(const unsigned char *mac, int l)
+{
+	int j;
+
+	for (j = 0; j < l; j++)
+		printf("%02x%s", mac[j],
+			(j==l-1) ? "" : ":");
+}
+
+void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
+{
+	int i;
+
+	print_mac(mac, l);
+	for (i = 0; i < l ; i++)
+		if (mask[i] != 255)
+			break;
+	if (i == l)
+		return;
+	printf("/");
+	print_mac(mask, l);
+}
+
+/*********************************************/
+/* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
+/*********************************************/
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+	static struct in_addr addr;
+	unsigned char *addrp;
+	char *p, *q;
+	unsigned int onebyte;
+	int i;
+	char buf[20];
+
+	/* copy dotted string, because we need to modify it */
+	strncpy(buf, dotted, sizeof(buf) - 1);
+	addrp = (unsigned char *) &(addr.s_addr);
+
+	p = buf;
+	for (i = 0; i < 3; i++) {
+		if ((q = strchr(p, '.')) == NULL)
+			return (struct in_addr *) NULL;
+
+		*q = '\0';
+		if (string_to_number(p, 0, 255, &onebyte) == -1)
+			return (struct in_addr *) NULL;
+
+		addrp[i] = (unsigned char) onebyte;
+		p = q + 1;
+	}
+
+	/* we've checked 3 bytes, now we check the last one */
+	if (string_to_number(p, 0, 255, &onebyte) == -1)
+		return (struct in_addr *) NULL;
+
+	addrp[3] = (unsigned char) onebyte;
+
+	return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+	struct netent *net;
+	static struct in_addr addr;
+
+	if ((net = getnetbyname(name)) != NULL) {
+		if (net->n_addrtype != AF_INET)
+			return (struct in_addr *) NULL;
+		addr.s_addr = htonl((unsigned long) net->n_net);
+		return &addr;
+	}
+
+	return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+	/* memcpy(dst, src, sizeof(struct in_addr)); */
+	dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	fprintf(stderr, "%s v%s: ", program_name, program_version);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+	if (status == PARAMETER_PROBLEM)
+		exit_tryhelp(status);
+	if (status == VERSION_PROBLEM)
+		fprintf(stderr,
+			"Perhaps arptables or your kernel needs to be upgraded.\n");
+	exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+			program_name, program_name );
+	exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+	struct arptables_match *m = NULL;
+	struct arptables_target *t = NULL;
+	int i;
+
+	printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+	       program_name, program_version, program_name, program_name,
+	       program_name, program_name, program_name, program_name,
+	       program_name, program_name);
+
+	printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain		Append to chain\n"
+"  --delete  -D chain		Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"				Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"				Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"				Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]		List the rules in a chain or all chains\n"
+"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]		Zero counters in chain or all chains\n"
+"  --new     -N chain		Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]		Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"				Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"				Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --source-ip	-s [!] address[/mask]\n"
+"				source specification\n"
+"  --destination-ip -d [!] address[/mask]\n"
+"				destination specification\n"
+"  --source-mac [!] address[/mask]\n"
+"  --destination-mac [!] address[/mask]\n"
+"  --h-length   -l   length[/mask] hardware length (nr of bytes)\n"
+"  --opcode code[/mask] operation code (2 bytes)\n"
+"  --h-type   type[/mask]  hardware type (2 bytes, hexadecimal)\n"
+"  --proto-type   type[/mask]  protocol type (2 bytes)\n"
+"  --in-interface -i [!] input name[+]\n"
+"				network interface name ([+] for wildcard)\n"
+"  --out-interface -o [!] output name[+]\n"
+"				network interface name ([+] for wildcard)\n"
+"  --jump	-j target\n"
+"				target for rule (may load target extension)\n"
+"  --match	-m match\n"
+"				extended match (may load extension)\n"
+"  --numeric	-n		numeric output of addresses and ports\n"
+"  --table	-t table	table to manipulate (default: `filter')\n"
+"  --verbose	-v		verbose mode\n"
+"  --line-numbers		print line numbers when listing\n"
+"  --exact	-x		expand numbers (display exact values)\n"
+"  --modprobe=<command>		try to insert modules using this command\n"
+"  --set-counters PKTS BYTES	set the counter during insert/append\n"
+"[!] --version	-V		print package version.\n");
+	printf(" opcode strings: \n");
+        for (i = 0; i < NUMOPCODES; i++)
+                printf(" %d = %s\n", i + 1, opcodes[i]);
+        printf(
+" hardware type string: 1 = Ethernet\n"
+" protocol type string: 0x800 = IPv4\n");
+
+	/* Print out any special helps. A user might like to be able
+	   to add a --help to the commandline, and see expected
+	   results. So we call help for all matches & targets */
+	for (t=arptables_targets;t;t=t->next) {
+		printf("\n");
+		t->help();
+	}
+	for (m=arptables_matches;m;m=m->next) {
+		printf("\n");
+		m->help();
+	}
+	exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+	int i, j, legal = 0;
+
+	/* Check that commands are valid with options.  Complicated by the
+	 * fact that if an option is legal with *any* command given, it is
+	 * legal overall (ie. -z and -l).
+	 */
+	for (i = 0; i < NUMBER_OF_OPT; i++) {
+		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+		for (j = 0; j < NUMBER_OF_CMD; j++) {
+			if (!(command & (1<<j)))
+				continue;
+
+			if (!(options & (1<<i))) {
+				if (commands_v_options[j][i] == '+')
+					exit_error(PARAMETER_PROBLEM,
+						   "You need to supply the `-%c' "
+						   "option for this command\n",
+						   optflags[i]);
+			} else {
+				if (commands_v_options[j][i] != 'x')
+					legal = 1;
+				else if (legal == 0)
+					legal = -1;
+			}
+		}
+		if (legal == -1)
+			exit_error(PARAMETER_PROBLEM,
+				   "Illegal option `-%c' with this command\n",
+				   optflags[i]);
+	}
+}
+
+static char
+opt2char(int option)
+{
+	const char *ptr;
+	for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+	const char *ptr;
+	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, int invert)
+{
+	if (invert)
+		exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+	if (*cmd & (~othercmds))
+		exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+	*cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert, int *optind, int argc)
+{
+	if (option && strcmp(option, "!") == 0) {
+		if (*invert)
+			exit_error(PARAMETER_PROBLEM,
+				   "Multiple `!' flags not allowed");
+		*invert = TRUE;
+		if (optind) {
+			*optind = *optind+1;
+			if (argc && *optind > argc)
+				exit_error(PARAMETER_PROBLEM,
+					   "no argument following `!'");
+		}
+
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+	void *p;
+
+	if ((p = calloc(count, size)) == NULL) {
+		perror("arptables: calloc failed");
+		exit(1);
+	}
+	return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+	void *p;
+
+	if ((p = malloc(size)) == NULL) {
+		perror("arptables: malloc failed");
+		exit(1);
+	}
+	return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+	struct hostent *host;
+	struct in_addr *addr;
+	unsigned int i;
+
+	*naddr = 0;
+	if ((host = gethostbyname(name)) != NULL) {
+		if (host->h_addrtype != AF_INET ||
+		    host->h_length != sizeof(struct in_addr))
+			return (struct in_addr *) NULL;
+
+		while (host->h_addr_list[*naddr] != (char *) NULL)
+			(*naddr)++;
+		addr = fw_calloc(*naddr, sizeof(struct in_addr));
+		for (i = 0; i < *naddr; i++)
+			inaddrcpy(&(addr[i]),
+				  (struct in_addr *) host->h_addr_list[i]);
+		return addr;
+	}
+
+	return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+	struct hostent *host;
+
+	if ((host = gethostbyaddr((char *) addr,
+				  sizeof(struct in_addr), AF_INET)) != NULL)
+		return (char *) host->h_name;
+
+	return (char *) NULL;
+}
+
+/*
+ *	All functions starting with "parse" should succeed, otherwise
+ *	the program fails.
+ *	Most routines return pointers to static data that may change
+ *	between calls to the same or other routines with a few exceptions:
+ *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *	return global static data.
+*/
+
+struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+	struct in_addr *addrp, *addrptmp;
+
+	if ((addrptmp = dotted_to_addr(name)) != NULL ||
+	    (addrptmp = network_to_addr(name)) != NULL) {
+		addrp = fw_malloc(sizeof(struct in_addr));
+		inaddrcpy(addrp, addrptmp);
+		*naddrs = 1;
+		return addrp;
+	}
+	if ((addrp = host_to_addr(name, naddrs)) != NULL)
+		return addrp;
+
+	exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+	static struct in_addr maskaddr;
+	struct in_addr *addrp;
+	unsigned int bits;
+
+	if (mask == NULL) {
+		/* no mask at all defaults to 32 bits */
+		maskaddr.s_addr = 0xFFFFFFFF;
+		return &maskaddr;
+	}
+	if ((addrp = dotted_to_addr(mask)) != NULL)
+		/* dotted_to_addr already returns a network byte order addr */
+		return addrp;
+	if (string_to_number(mask, 0, 32, &bits) == -1)
+		exit_error(PARAMETER_PROBLEM,
+			   "invalid mask `%s' specified", mask);
+	if (bits != 0) {
+		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+		return &maskaddr;
+	}
+
+	maskaddr.s_addr = 0L;
+	return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+		      struct in_addr *maskp, unsigned int *naddrs)
+{
+	struct in_addr *addrp;
+	char buf[256];
+	char *p;
+	int i, j, k, n;
+
+	strncpy(buf, name, sizeof(buf) - 1);
+	if ((p = strrchr(buf, '/')) != NULL) {
+		*p = '\0';
+		addrp = parse_mask(p + 1);
+	} else
+		addrp = parse_mask(NULL);
+	inaddrcpy(maskp, addrp);
+
+	/* if a null mask is given, the name is ignored, like in "any/0" */
+	if (maskp->s_addr == 0L)
+		strcpy(buf, "0.0.0.0");
+
+	addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+	n = *naddrs;
+	for (i = 0, j = 0; i < n; i++) {
+		addrp[j++].s_addr &= maskp->s_addr;
+		for (k = 0; k < j - 1; k++) {
+			if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+				(*naddrs)--;
+				j--;
+				break;
+			}
+		}
+	}
+}
+
+struct arptables_match *
+find_match(const char *name, enum arpt_tryload tryload)
+{
+	struct arptables_match *ptr;
+
+	for (ptr = arptables_matches; ptr; ptr = ptr->next) {
+		if (strcmp(name, ptr->name) == 0)
+			break;
+	}
+
+	if (ptr && !ptr->loaded) {
+		if (tryload != DONT_LOAD)
+			ptr->loaded = 1;
+		else
+			ptr = NULL;
+	}
+	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+		exit_error(PARAMETER_PROBLEM,
+			   "Couldn't find match `%s'\n", name);
+	}
+
+	if (ptr)
+		ptr->used = 1;
+
+	return ptr;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+	int vialen = strlen(arg);
+	unsigned int i;
+
+	memset(mask, 0, IFNAMSIZ);
+	memset(vianame, 0, IFNAMSIZ);
+
+	if (vialen + 1 > IFNAMSIZ)
+		exit_error(PARAMETER_PROBLEM,
+			   "interface name `%s' must be shorter than IFNAMSIZ"
+			   " (%i)", arg, IFNAMSIZ-1);
+
+	strcpy(vianame, arg);
+	if (vialen == 0)
+		memset(mask, 0, IFNAMSIZ);
+	else if (vianame[vialen - 1] == '+') {
+		memset(mask, 0xFF, vialen - 1);
+		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+		/* Don't remove `+' here! -HW */
+	} else {
+		/* Include nul-terminator in match */
+		memset(mask, 0xFF, vialen + 1);
+		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+		for (i = 0; vianame[i]; i++) {
+			if (!isalnum(vianame[i]) 
+			    && vianame[i] != '_' 
+			    && vianame[i] != '.') {
+				printf("Warning: wierd character in interface"
+				       " `%s' (No aliases, :, ! or *).\n",
+				       vianame);
+				break;
+			}
+		}
+	}
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+	unsigned int rulenum;
+
+	if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+		exit_error(PARAMETER_PROBLEM,
+			   "Invalid rule number `%s'", rule);
+
+	return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+	const char *ptr;
+
+	if (strlen(targetname) < 1)
+		exit_error(PARAMETER_PROBLEM,
+			   "Invalid target name (too short)");
+
+	if (strlen(targetname)+1 > sizeof(arpt_chainlabel))
+		exit_error(PARAMETER_PROBLEM,
+			   "Invalid target name `%s' (%zu chars max)",
+			   targetname, sizeof(arpt_chainlabel)-1);
+
+	for (ptr = targetname; *ptr; ptr++)
+		if (isspace(*ptr))
+			exit_error(PARAMETER_PROBLEM,
+				   "Invalid target name `%s'", targetname);
+	return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+	struct netent *net;
+
+	if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+		return (char *) net->n_name;
+
+	return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+	static char buf[20];
+	const unsigned char *bytep;
+
+	bytep = (const unsigned char *) &(addrp->s_addr);
+	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+	return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+	char *name;
+
+	if ((name = addr_to_host(addr)) != NULL ||
+	    (name = addr_to_network(addr)) != NULL)
+		return name;
+
+	return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+	int i;
+	static char buf[20];
+	u_int32_t maskaddr, bits;
+
+	maskaddr = ntohl(mask->s_addr);
+
+	if (maskaddr == 0xFFFFFFFFL)
+		/* we don't want to see "/32" */
+		return "";
+
+	i = 32;
+	bits = 0xFFFFFFFEL;
+	while (--i >= 0 && maskaddr != bits)
+		bits <<= 1;
+	if (i >= 0)
+		sprintf(buf, "/%d", i);
+	else
+		/* mask was not a decent combination of 1's and 0's */
+		sprintf(buf, "/%s", addr_to_dotted(mask));
+
+	return buf;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+		 unsigned int *ret)
+{
+	long number;
+	char *end;
+
+	/* Handle hex, octal, etc. */
+	errno = 0;
+	number = strtol(s, &end, 0);
+	if (*end == '\0' && end != s) {
+		/* we parsed a number, let's see if we want this */
+		if (errno != ERANGE && min <= number && number <= max) {
+			*ret = number;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int16_t *invflg,
+	   int invert)
+{
+	if (*options & option)
+		exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+			   opt2char(option));
+	*options |= option;
+
+	if (invert) {
+		unsigned int i;
+		for (i = 0; 1 << i != option; i++);
+
+		if (!inverse_for_options[i])
+			exit_error(PARAMETER_PROBLEM,
+				   "cannot have ! before -%c",
+				   opt2char(option));
+		*invflg |= inverse_for_options[i];
+	}
+}
+
+struct arptables_target *
+find_target(const char *name, enum arpt_tryload tryload)
+{
+	struct arptables_target *ptr;
+
+	/* Standard target? */
+	if (strcmp(name, "") == 0
+	    || strcmp(name, ARPTC_LABEL_ACCEPT) == 0
+	    || strcmp(name, ARPTC_LABEL_DROP) == 0
+	    || strcmp(name, ARPTC_LABEL_QUEUE) == 0
+	    || strcmp(name, ARPTC_LABEL_RETURN) == 0)
+		name = "standard";
+
+	for (ptr = arptables_targets; ptr; ptr = ptr->next) {
+		if (strcmp(name, ptr->name) == 0)
+			break;
+	}
+
+	if (ptr && !ptr->loaded) {
+		if (tryload != DONT_LOAD)
+			ptr->loaded = 1;
+		else
+			ptr = NULL;
+	}
+	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+		exit_error(PARAMETER_PROBLEM,
+			   "Couldn't find target `%s'\n", name);
+	}
+
+	if (ptr)
+		ptr->used = 1;
+
+	return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+	      unsigned int *option_offset)
+{
+	unsigned int num_old, num_new, i;
+	struct option *merge;
+
+	for (num_old = 0; oldopts[num_old].name; num_old++);
+	for (num_new = 0; newopts[num_new].name; num_new++);
+
+	global_option_offset += OPTION_OFFSET;
+	*option_offset = global_option_offset;
+
+	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+	memcpy(merge, oldopts, num_old * sizeof(struct option));
+	for (i = 0; i < num_new; i++) {
+		merge[num_old + i] = newopts[i];
+		merge[num_old + i].val += *option_offset;
+	}
+	memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+	return merge;
+}
+
+void
+register_match(struct arptables_match *me)
+{
+	struct arptables_match **i;
+
+	if (strcmp(me->version, program_version) != 0) {
+		fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+			program_name, me->name, me->version, program_version);
+		exit(1);
+	}
+
+	if (find_match(me->name, DONT_LOAD)) {
+		fprintf(stderr, "%s: match `%s' already registered.\n",
+			program_name, me->name);
+		exit(1);
+	}
+
+	if (me->size != ARPT_ALIGN(me->size)) {
+		fprintf(stderr, "%s: match `%s' has invalid size %zu.\n",
+			program_name, me->name, me->size);
+		exit(1);
+	}
+
+	/* Append to list. */
+	for (i = &arptables_matches; *i; i = &(*i)->next);
+	me->next = NULL;
+	*i = me;
+
+	me->m = NULL;
+	me->mflags = 0;
+}
+
+void
+register_target(struct arptables_target *me)
+{
+	if (strcmp(me->version, program_version) != 0) {
+		fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+			program_name, me->name, me->version, program_version);
+		exit(1);
+	}
+
+	if (find_target(me->name, DONT_LOAD)) {
+		fprintf(stderr, "%s: target `%s' already registered.\n",
+			program_name, me->name);
+		exit(1);
+	}
+
+	if (me->size != ARPT_ALIGN(me->size)) {
+		fprintf(stderr, "%s: target `%s' has invalid size %zu.\n",
+			program_name, me->name, me->size);
+		exit(1);
+	}
+
+	/* Prepend to list. */
+	me->next = arptables_targets;
+	arptables_targets = me;
+	me->t = NULL;
+	me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+	if (format & FMT_KILOMEGAGIGA) {
+		if (number > 99999) {
+			number = (number + 500) / 1000;
+			if (number > 9999) {
+				number = (number + 500) / 1000;
+				if (number > 9999) {
+					number = (number + 500) / 1000;
+					if (number > 9999) {
+						number = (number + 500) / 1000;
+						printf(FMT("%4"PRIu64"T ","%"PRIu64"T "), number);
+					}
+					else printf(FMT("%4"PRIu64"G ","%"PRIu64"G "), number);
+				}
+				else printf(FMT("%4"PRIu64"M ","%"PRIu64"M "), number);
+			} else
+				printf(FMT("%4"PRIu64"K ","%"PRIu64"K "), number);
+		} else
+			printf(FMT("%5"PRIu64" ","%"PRIu64" "), number);
+	} else
+		printf(FMT("%8"PRIu64" ","%"PRIu64" "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, arptc_handle_t *handle)
+{
+	struct arpt_counters counters;
+	const char *pol = arptc_get_policy(chain, &counters, handle);
+	printf("Chain %s", chain);
+	if (pol) {
+		printf(" (policy %s", pol);
+		if (!(format & FMT_NOCOUNTS)) {
+			fputc(' ', stdout);
+			print_num(counters.pcnt, (format|FMT_NOTABLE));
+			fputs("packets, ", stdout);
+			print_num(counters.bcnt, (format|FMT_NOTABLE));
+			fputs("bytes", stdout);
+		}
+		printf(")\n");
+	} else {
+		unsigned int refs;
+		if (!arptc_get_references(&refs, chain, handle))
+			printf(" (ERROR obtaining refs)\n");
+		else
+			printf(" (%u references)\n", refs);
+	}
+
+/* I don't like this
+	if (format & FMT_LINENUMBERS)
+		printf(FMT("%-4s ", "%s "), "num");
+	if (!(format & FMT_NOCOUNTS)) {
+		if (format & FMT_KILOMEGAGIGA) {
+			printf(FMT("%5s ","%s "), "pkts");
+			printf(FMT("%5s ","%s "), "bytes");
+		} else {
+			printf(FMT("%8s ","%s "), "pkts");
+			printf(FMT("%10s ","%s "), "bytes");
+		}
+	}
+	if (!(format & FMT_NOTARGET))
+		printf(FMT("%-9s ","%s "), "target");
+	fputs(" prot ", stdout);
+	if (format & FMT_OPTIONS)
+		fputs("opt", stdout);
+	if (format & FMT_VIA) {
+		printf(FMT(" %-6s ","%s "), "in");
+		printf(FMT("%-6s ","%s "), "out");
+	}
+	printf(FMT(" %-19s ","%s "), "source");
+	printf(FMT(" %-19s "," %s "), "destination");
+	printf("\n");
+*/
+}
+
+/*
+static int
+print_match(const struct arpt_entry_match *m,
+	    const struct arpt_arp *arp,
+	    int numeric)
+{
+	struct arptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+	if (match) {
+		if (match->print)
+			match->print(arp, m, numeric);
+		else
+			printf("%s ", match->name);
+	} else {
+		if (m->u.user.name[0])
+			printf("UNKNOWN match `%s' ", m->u.user.name);
+	}
+*/
+	/* Don't stop iterating. */
+/*	return 0;
+}
+*/
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct arpt_entry *fw,
+	       const char *targname,
+	       unsigned int num,
+	       unsigned int format,
+	       const arptc_handle_t handle)
+{
+	struct arptables_target *target = NULL;
+	const struct arpt_entry_target *t;
+	u_int8_t flags;
+	char buf[BUFSIZ];
+	int i;
+	char iface[IFNAMSIZ+2];
+	int print_iface = 0;
+
+	if (!arptc_is_chain(targname, handle))
+		target = find_target(targname, TRY_LOAD);
+	else
+		target = find_target(ARPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+	t = arpt_get_target((struct arpt_entry *)fw);
+	flags = fw->arp.flags;
+
+	if (format & FMT_LINENUMBERS)
+		printf("%u ", num+1);
+
+	if (!(format & FMT_NOTARGET) && targname[0] != '\0')
+		printf("-j %s ", targname);
+
+	if (fw->arp.invflags & ARPT_INV_VIA_IN) {
+		iface[0] = '!';
+		iface[1] = '\0';
+		print_iface = 1;
+	}
+	else iface[0] = '\0';
+
+	if (fw->arp.iniface[0] != '\0') {
+		strcat(iface, fw->arp.iniface);
+		print_iface = 1;
+	}
+	else if (format & FMT_VIA) {
+		print_iface = 1;
+		if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+	}
+	if (print_iface)
+		printf("-i %s ", iface);
+
+	print_iface = 0;
+	if (fw->arp.invflags & ARPT_INV_VIA_OUT) {
+		iface[0] = '!';
+		iface[1] = '\0';
+		print_iface = 1;
+	}
+	else iface[0] = '\0';
+
+	if (fw->arp.outiface[0] != '\0') {
+		strcat(iface, fw->arp.outiface);
+		print_iface = 1;
+	}
+	else if (format & FMT_VIA) {
+		print_iface = 1;
+		if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+	}
+	if (print_iface)
+		printf("-o %s ", iface);
+
+	if (fw->arp.smsk.s_addr != 0L) {
+		printf("%s", fw->arp.invflags & ARPT_INV_SRCIP
+			? "! " : "");
+		if (format & FMT_NUMERIC)
+			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
+		else
+			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
+		strcat(buf, mask_to_dotted(&(fw->arp.smsk)));
+		printf("-s %s ", buf);
+	}
+
+	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
+		if (fw->arp.src_devaddr.mask[i] != 0)
+			break;
+	if (i == ARPT_DEV_ADDR_LEN_MAX)
+		goto after_devsrc;
+	printf("%s", fw->arp.invflags & ARPT_INV_SRCDEVADDR
+		? "! " : "");
+	printf("--src-mac ");
+	print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
+		(unsigned char *)fw->arp.src_devaddr.mask, ETH_ALEN);
+	printf(" ");
+after_devsrc:
+
+	if (fw->arp.tmsk.s_addr != 0L) {
+		printf("%s",fw->arp.invflags & ARPT_INV_TGTIP
+			? "! " : "");
+		if (format & FMT_NUMERIC)
+			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
+		else
+			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
+		strcat(buf, mask_to_dotted(&(fw->arp.tmsk)));
+		printf("-d %s ", buf);
+	}
+
+	for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
+		if (fw->arp.tgt_devaddr.mask[i] != 0)
+			break;
+	if (i == ARPT_DEV_ADDR_LEN_MAX)
+		goto after_devdst;
+	printf("%s",fw->arp.invflags & ARPT_INV_TGTDEVADDR
+		? "! " : "");
+	printf("--dst-mac ");
+	print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
+		(unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN);
+	printf(" ");
+after_devdst:
+
+	if (fw->arp.arhln_mask != 0) {
+		printf("%s",fw->arp.invflags & ARPT_INV_ARPHLN
+			? "! " : "");
+		printf("--h-length %d", fw->arp.arhln);
+		if (fw->arp.arhln_mask != 255)
+			printf("/%d", fw->arp.arhln_mask);
+		printf(" ");
+	}
+
+	if (fw->arp.arpop_mask != 0) {
+		int tmp = ntohs(fw->arp.arpop);
+
+		printf("%s",fw->arp.invflags & ARPT_INV_ARPOP
+			? "! " : "");
+		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
+			printf("--opcode %s", opcodes[tmp-1]);
+		else
+			printf("--opcode %d", tmp);
+		if (fw->arp.arpop_mask != 65535)
+			printf("/%d", ntohs(fw->arp.arpop_mask));
+		printf(" ");
+	}
+
+	if (fw->arp.arhrd_mask != 0) {
+		uint16_t tmp = ntohs(fw->arp.arhrd);
+
+		printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD
+			? "! " : "");
+		if (tmp == 1 && !(format & FMT_NUMERIC))
+			printf("--h-type %s", "Ethernet");
+		else
+			printf("--h-type %u", tmp);
+		if (fw->arp.arhrd_mask != 65535)
+			printf("/%d", ntohs(fw->arp.arhrd_mask));
+		printf(" ");
+	}
+
+	if (fw->arp.arpro_mask != 0) {
+		int tmp = ntohs(fw->arp.arpro);
+
+		printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO
+			? "! " : "");
+		if (tmp == 0x0800 && !(format & FMT_NUMERIC))
+			printf("--proto-type %s", "IPv4");
+		else
+			printf("--proto-type 0x%x", tmp);
+		if (fw->arp.arpro_mask != 65535)
+			printf("/%x", ntohs(fw->arp.arpro_mask));
+		printf(" ");
+	}
+
+/* FIXME
+	ARPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+*/
+
+	if (target) {
+		if (target->print)
+			/* Print the target information. */
+			target->print(&fw->arp, t, format & FMT_NUMERIC);
+	} else if (t->u.target_size != sizeof(*t))
+		printf("[%zu bytes of unknown target data] ",
+		       t->u.target_size - sizeof(*t));
+
+	if (!(format & FMT_NOCOUNTS)) {
+		printf(", pcnt=");
+		print_num(fw->counters.pcnt, format);
+		printf("-- bcnt=");
+		print_num(fw->counters.bcnt, format);
+	}
+
+	if (!(format & FMT_NONEWLINE))
+		fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct arpt_entry *fw,
+		    const arptc_handle_t h)
+{
+	struct arpt_entry_target *t;
+
+	t = arpt_get_target((struct arpt_entry *)fw);
+	print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const arpt_chainlabel chain,
+	     struct arpt_entry *fw,
+	     unsigned int nsaddrs,
+	     const struct in_addr saddrs[],
+	     unsigned int ndaddrs,
+	     const struct in_addr daddrs[],
+	     int verbose,
+	     arptc_handle_t *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->arp.src.s_addr = saddrs[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->arp.tgt.s_addr = daddrs[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, *handle);
+			ret &= arptc_append_entry(chain, fw, handle);
+		}
+	}
+
+	return ret;
+}
+
+static int
+replace_entry(const arpt_chainlabel chain,
+	      struct arpt_entry *fw,
+	      unsigned int rulenum,
+	      const struct in_addr *saddr,
+	      const struct in_addr *daddr,
+	      int verbose,
+	      arptc_handle_t *handle)
+{
+	fw->arp.src.s_addr = saddr->s_addr;
+	fw->arp.tgt.s_addr = daddr->s_addr;
+
+	if (verbose)
+		print_firewall_line(fw, *handle);
+	return arptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const arpt_chainlabel chain,
+	     struct arpt_entry *fw,
+	     unsigned int rulenum,
+	     unsigned int nsaddrs,
+	     const struct in_addr saddrs[],
+	     unsigned int ndaddrs,
+	     const struct in_addr daddrs[],
+	     int verbose,
+	     arptc_handle_t *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->arp.src.s_addr = saddrs[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->arp.tgt.s_addr = daddrs[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, *handle);
+			ret &= arptc_insert_entry(chain, fw, rulenum, handle);
+		}
+	}
+
+	return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct arpt_entry *fw)
+{
+	/* Establish mask for comparison */
+	unsigned int size;
+	struct arptables_match *m;
+	unsigned char *mask, *mptr;
+
+	size = sizeof(struct arpt_entry);
+	for (m = arptables_matches; m; m = m->next) {
+		if (!m->used)
+			continue;
+
+		size += ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size;
+	}
+
+	mask = fw_calloc(1, size
+			 + ARPT_ALIGN(sizeof(struct arpt_entry_target))
+			 + arptables_targets->size);
+
+	memset(mask, 0xFF, sizeof(struct arpt_entry));
+	mptr = mask + sizeof(struct arpt_entry);
+
+	for (m = arptables_matches; m; m = m->next) {
+		if (!m->used)
+			continue;
+
+		memset(mptr, 0xFF,
+		       ARPT_ALIGN(sizeof(struct arpt_entry_match))
+		       + m->userspacesize);
+		mptr += ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size;
+	}
+
+	memset(mptr, 0xFF,
+	       ARPT_ALIGN(sizeof(struct arpt_entry_target))
+	       + arptables_targets->userspacesize);
+
+	return mask;
+}
+
+static int
+delete_entry(const arpt_chainlabel chain,
+	     struct arpt_entry *fw,
+	     unsigned int nsaddrs,
+	     const struct in_addr saddrs[],
+	     unsigned int ndaddrs,
+	     const struct in_addr daddrs[],
+	     int verbose,
+	     arptc_handle_t *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+	unsigned char *mask;
+
+	mask = make_delete_mask(fw);
+	for (i = 0; i < nsaddrs; i++) {
+		fw->arp.src.s_addr = saddrs[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->arp.tgt.s_addr = daddrs[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, *handle);
+			ret &= arptc_delete_entry(chain, fw, mask, handle);
+		}
+	}
+	return ret;
+}
+
+int
+for_each_chain(int (*fn)(const arpt_chainlabel, int, arptc_handle_t *),
+	       int verbose, int builtinstoo, arptc_handle_t *handle)
+{
+        int ret = 1;
+	const char *chain;
+	char *chains;
+	unsigned int i, chaincount = 0;
+
+	chain = arptc_first_chain(handle);
+	while (chain) {
+		chaincount++;
+		chain = arptc_next_chain(handle);
+        }
+
+	chains = fw_malloc(sizeof(arpt_chainlabel) * chaincount);
+	i = 0;
+	chain = arptc_first_chain(handle);
+	while (chain) {
+		strcpy(chains + i*sizeof(arpt_chainlabel), chain);
+		i++;
+		chain = arptc_next_chain(handle);
+        }
+
+	for (i = 0; i < chaincount; i++) {
+		if (!builtinstoo
+		    && arptc_builtin(chains + i*sizeof(arpt_chainlabel),
+				    *handle))
+			continue;
+	        ret &= fn(chains + i*sizeof(arpt_chainlabel), verbose, handle);
+	}
+
+	free(chains);
+        return ret;
+}
+
+int
+flush_entries(const arpt_chainlabel chain, int verbose,
+	      arptc_handle_t *handle)
+{
+	if (!chain)
+		return for_each_chain(flush_entries, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Flushing chain `%s'\n", chain);
+	return arptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const arpt_chainlabel chain, int verbose,
+	     arptc_handle_t *handle)
+{
+	if (!chain)
+		return for_each_chain(zero_entries, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Zeroing chain `%s'\n", chain);
+	return arptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const arpt_chainlabel chain, int verbose,
+	     arptc_handle_t *handle)
+{
+	if (!chain)
+		return for_each_chain(delete_chain, verbose, 0, handle);
+
+	if (verbose)
+	        fprintf(stdout, "Deleting chain `%s'\n", chain);
+	return arptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const arpt_chainlabel chain, int verbose, int numeric,
+	     int expanded, int linenumbers, arptc_handle_t *handle)
+{
+	int found = 0;
+	unsigned int format;
+	const char *this;
+
+	format = FMT_OPTIONS;
+	if (!verbose)
+		format |= FMT_NOCOUNTS;
+	else
+		format |= FMT_VIA;
+
+	if (numeric)
+		format |= FMT_NUMERIC;
+
+	if (!expanded)
+		format |= FMT_KILOMEGAGIGA;
+
+	if (linenumbers)
+		format |= FMT_LINENUMBERS;
+
+	for (this = arptc_first_chain(handle);
+	     this;
+	     this = arptc_next_chain(handle)) {
+		const struct arpt_entry *i;
+		unsigned int num;
+
+		if (chain && strcmp(chain, this) != 0)
+			continue;
+
+		if (found) printf("\n");
+
+		print_header(format, this, handle);
+		i = arptc_first_rule(this, handle);
+
+		num = 0;
+		while (i) {
+			print_firewall(i,
+				       arptc_get_target(i, handle),
+				       num++,
+				       format,
+				       *handle);
+			i = arptc_next_rule(i, handle);
+		}
+		found = 1;
+	}
+
+	errno = ENOENT;
+	return found;
+}
+
+static char *get_modprobe(void)
+{
+	int procfile;
+	char *ret;
+
+	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+	if (procfile < 0)
+		return NULL;
+
+	ret = malloc(1024);
+	if (ret) {
+		switch (read(procfile, ret, 1024)) {
+		case -1: goto fail;
+		case 1024: goto fail; /* Partial read.  Wierd */
+		}
+		if (ret[strlen(ret)-1]=='\n')
+			ret[strlen(ret)-1]=0;
+		close(procfile);
+		return ret;
+	}
+ fail:
+	free(ret);
+	close(procfile);
+	return NULL;
+}
+
+int arptables_insmod(const char *modname, const char *modprobe)
+{
+	char *buf = NULL;
+	char *argv[3];
+
+	/* If they don't explicitly set it, read out of kernel */
+	if (!modprobe) {
+		buf = get_modprobe();
+		if (!buf)
+			return -1;
+		modprobe = buf;
+	}
+
+	switch (fork()) {
+	case 0:
+		argv[0] = (char *)modprobe;
+		argv[1] = (char *)modname;
+		argv[2] = NULL;
+		execv(argv[0], argv);
+
+		/* not usually reached */
+		exit(0);
+	case -1:
+		return -1;
+
+	default: /* parent */
+		wait(NULL);
+	}
+
+	free(buf);
+	return 0;
+}
+
+static struct arpt_entry *
+generate_entry(const struct arpt_entry *fw,
+	       struct arptables_match *matches,
+	       struct arpt_entry_target *target)
+{
+	unsigned int size;
+	/*
+	struct arptables_match *m;
+	*/
+	struct arpt_entry *e;
+
+	size = sizeof(struct arpt_entry);
+	/* FIXME
+	for (m = matches; m; m = m->next) {
+		if (!m->used)
+			continue;
+
+		size += m->m->u.match_size;
+	}
+	*/
+
+	e = fw_malloc(size + target->u.target_size);
+	*e = *fw;
+	e->target_offset = size;
+	e->next_offset = size + target->u.target_size;
+
+	size = 0;
+	/* FIXME
+	for (m = matches; m; m = m->next) {
+		if (!m->used)
+			continue;
+
+		memcpy(e->elems + size, m->m, m->m->u.match_size);
+		size += m->m->u.match_size;
+	}
+	*/
+
+	memcpy(e->elems + size, target, target->u.target_size);
+
+	return e;
+}
+
+int do_command(int argc, char *argv[], char **table, arptc_handle_t *handle)
+{
+	struct arpt_entry fw, *e = NULL;
+	int invert = 0;
+	unsigned int nsaddrs = 0, ndaddrs = 0;
+	struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+	int c, verbose = 0;
+	const char *chain = NULL;
+	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+	const char *policy = NULL, *newname = NULL;
+	unsigned int rulenum = 0, options = 0, command = 0;
+	const char *pcnt = NULL, *bcnt = NULL;
+	int ret = 1;
+/*	struct arptables_match *m;*/
+	struct arptables_target *target = NULL;
+	struct arptables_target *t;
+	const char *jumpto = "";
+	char *protocol = NULL;
+	const char *modprobe = NULL;
+
+	/* first figure out if this is a 2.6 or a 2.4 kernel */
+	*handle = arptc_init(*table);
+
+	if (!*handle) {
+		arptables_insmod("arp_tables", modprobe);
+		*handle = arptc_init(*table);
+		if (!*handle) {
+			RUNTIME_NF_ARP_NUMHOOKS = 2;
+			*handle = arptc_init(*table);
+			if (!*handle) {
+				exit_error(VERSION_PROBLEM,
+				"can't initialize arptables table `%s': %s",
+				*table, arptc_strerror(errno));
+			}
+		}
+        }
+
+	memset(&fw, 0, sizeof(fw));
+	opts = original_opts;
+	global_option_offset = 0;
+
+	/* re-set optind to 0 in case do_command gets called
+	 * a second time */
+	optind = 0;
+
+	/* clear mflags in case do_command gets called a second time
+	 * (we clear the global list of all matches for security)*/
+/*	for (m = arptables_matches; m; m = m->next) {
+		m->mflags = 0;
+		m->used = 0;
+	}*/
+
+	for (t = arptables_targets; t; t = t->next) {
+		t->tflags = 0;
+		t->used = 0;
+	}
+
+	/* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+	opterr = 0;
+
+	while ((c = getopt_long(argc, argv,
+	   "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
+					   opts, NULL)) != -1) {
+		switch (c) {
+			/*
+			 * Command selection
+			 */
+		case 'A':
+			add_command(&command, CMD_APPEND, CMD_NONE,
+				    invert);
+			chain = optarg;
+			break;
+
+		case 'D':
+			add_command(&command, CMD_DELETE, CMD_NONE,
+				    invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!') {
+				rulenum = parse_rulenumber(argv[optind++]);
+				command = CMD_DELETE_NUM;
+			}
+			break;
+
+		case 'R':
+			add_command(&command, CMD_REPLACE, CMD_NONE,
+				    invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else
+				exit_error(PARAMETER_PROBLEM,
+					   "-%c requires a rule number",
+					   cmd2char(CMD_REPLACE));
+			break;
+
+		case 'I':
+			add_command(&command, CMD_INSERT, CMD_NONE,
+				    invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else rulenum = 1;
+			break;
+
+		case 'L':
+			add_command(&command, CMD_LIST, CMD_ZERO,
+				    invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'F':
+			add_command(&command, CMD_FLUSH, CMD_NONE,
+				    invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'Z':
+			add_command(&command, CMD_ZERO, CMD_LIST,
+				    invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				&& argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'N':
+			if (optarg && *optarg == '-')
+				exit_error(PARAMETER_PROBLEM,
+					   "chain name not allowed to start "
+					   "with `-'\n");
+			if (find_target(optarg, TRY_LOAD))
+				exit_error(PARAMETER_PROBLEM,
+					   "chain name may not clash "
+					   "with target name\n");
+			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+				    invert);
+			chain = optarg;
+			break;
+
+		case 'X':
+			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+				    invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'E':
+			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+				    invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				newname = argv[optind++];
+			else
+				exit_error(PARAMETER_PROBLEM,
+				           "-%c requires old-chain-name and "
+					   "new-chain-name",
+					    cmd2char(CMD_RENAME_CHAIN));
+			break;
+
+		case 'P':
+			add_command(&command, CMD_SET_POLICY, CMD_NONE,
+				    invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				policy = argv[optind++];
+			else
+				exit_error(PARAMETER_PROBLEM,
+					   "-%c requires a chain and a policy",
+					   cmd2char(CMD_SET_POLICY));
+			break;
+
+		case 'h':
+			if (!optarg)
+				optarg = argv[optind];
+
+			/* arptables -p icmp -h */
+			if (!arptables_matches && protocol)
+				find_match(protocol, TRY_LOAD);
+
+			exit_printhelp();
+
+		case 's':
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_S_IP, &fw.arp.invflags,
+				   invert);
+			shostnetworkmask = argv[optind-1];
+			break;
+
+		case 'd':
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_D_IP, &fw.arp.invflags,
+				   invert);
+			dhostnetworkmask = argv[optind-1];
+			break;
+
+		case 2:/* src-mac */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_S_MAC, &fw.arp.invflags,
+				   invert);
+			if (getmac_and_mask(argv[optind - 1],
+			   fw.arp.src_devaddr.addr, fw.arp.src_devaddr.mask))
+				exit_error(PARAMETER_PROBLEM, "Problem with specified "
+				            "source mac");
+			break;
+
+		case 3:/* dst-mac */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_D_MAC, &fw.arp.invflags,
+				   invert);
+
+			if (getmac_and_mask(argv[optind - 1],
+			   fw.arp.tgt_devaddr.addr, fw.arp.tgt_devaddr.mask))
+				exit_error(PARAMETER_PROBLEM, "Problem with specified "
+				            "destination mac");
+			break;
+
+		case 'l':/* hardware length */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_H_LENGTH, &fw.arp.invflags,
+				   invert);
+			getlength_and_mask(argv[optind - 1], &fw.arp.arhln,
+					   &fw.arp.arhln_mask);
+			break;
+
+		case 8:/* protocol length */
+		exit_error(PARAMETER_PROBLEM, "not supported");
+/*
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_P_LENGTH, &fw.arp.invflags,
+				   invert);
+
+			getlength_and_mask(argv[optind - 1], &fw.arp.arpln,
+					   &fw.arp.arpln_mask);
+			break;
+*/
+
+		case 4:/* opcode */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_OPCODE, &fw.arp.invflags,
+				   invert);
+			if (get16_and_mask(argv[optind - 1], &fw.arp.arpop, &fw.arp.arpop_mask, 10)) {
+				int i;
+
+				for (i = 0; i < NUMOPCODES; i++)
+					if (!strcasecmp(opcodes[i], optarg))
+						break;
+				if (i == NUMOPCODES)
+					exit_error(PARAMETER_PROBLEM, "Problem with specified opcode");
+				fw.arp.arpop = htons(i+1);
+			}
+			break;
+
+		case 5:/* h-type */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_H_TYPE, &fw.arp.invflags,
+				   invert);
+			if (get16_and_mask(argv[optind - 1], &fw.arp.arhrd, &fw.arp.arhrd_mask, 16)) {
+				if (strcasecmp(argv[optind-1], "Ethernet"))
+					exit_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
+				fw.arp.arhrd = htons(1);
+			}
+			break;
+
+		case 6:/* proto-type */
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_P_TYPE, &fw.arp.invflags,
+				   invert);
+			if (get16_and_mask(argv[optind - 1], &fw.arp.arpro, &fw.arp.arpro_mask, 0)) {
+				if (strcasecmp(argv[optind-1], "ipv4"))
+					exit_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
+				fw.arp.arpro = htons(0x800);
+			}
+			break;
+
+		case 'j':
+			set_option(&options, OPT_JUMP, &fw.arp.invflags,
+				   invert);
+			jumpto = parse_target(optarg);
+			/* TRY_LOAD (may be chain name) */
+			target = find_target(jumpto, TRY_LOAD);
+
+			if (target) {
+				size_t size;
+
+				size = ARPT_ALIGN(sizeof(struct arpt_entry_target))
+					+ target->size;
+
+				target->t = fw_calloc(1, size);
+				target->t->u.target_size = size;
+				strcpy(target->t->u.user.name, jumpto);
+/*
+				target->init(target->t, &fw.nfcache);
+*/
+				target->init(target->t);
+
+				opts = merge_options(opts, target->extra_opts, &target->option_offset);
+			}
+			break;
+
+
+		case 'i':
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_VIANAMEIN, &fw.arp.invflags,
+				   invert);
+			parse_interface(argv[optind-1],
+					fw.arp.iniface,
+					fw.arp.iniface_mask);
+/*			fw.nfcache |= NFC_IP_IF_IN; */
+			break;
+
+		case 'o':
+			check_inverse(optarg, &invert, &optind, argc);
+			set_option(&options, OPT_VIANAMEOUT, &fw.arp.invflags,
+				   invert);
+			parse_interface(argv[optind-1],
+					fw.arp.outiface,
+					fw.arp.outiface_mask);
+			/* fw.nfcache |= NFC_IP_IF_OUT; */
+			break;
+
+		case 'v':
+			if (!verbose)
+				set_option(&options, OPT_VERBOSE,
+					   &fw.arp.invflags, invert);
+			verbose++;
+			break;
+
+		case 'm': /*{
+			size_t size;
+
+			if (invert)
+				exit_error(PARAMETER_PROBLEM,
+					   "unexpected ! flag before --match");
+
+			m = find_match(optarg, LOAD_MUST_SUCCEED);
+			size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
+					 + m->size;
+			m->m = fw_calloc(1, size);
+			m->m->u.match_size = size;
+			strcpy(m->m->u.user.name, m->name);
+			m->init(m->m, &fw.nfcache);
+			opts = merge_options(opts, m->extra_opts, &m->option_offset);
+		}*/
+		break;
+
+		case 'n':
+			set_option(&options, OPT_NUMERIC, &fw.arp.invflags,
+				   invert);
+			break;
+
+		case 't':
+			if (invert)
+				exit_error(PARAMETER_PROBLEM,
+					   "unexpected ! flag before --table");
+			*table = argv[optind-1];
+			break;
+
+		case 'V':
+			if (invert)
+				printf("Not %s ;-)\n", program_version);
+			else
+				printf("%s v%s\n",
+				       program_name, program_version);
+			exit(0);
+
+		case '0':
+			set_option(&options, OPT_LINENUMBERS, &fw.arp.invflags,
+				   invert);
+			break;
+
+		case 'M':
+			modprobe = optarg;
+			break;
+
+		case 'c':
+
+			set_option(&options, OPT_COUNTERS, &fw.arp.invflags,
+				   invert);
+			pcnt = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				bcnt = argv[optind++];
+			else
+				exit_error(PARAMETER_PROBLEM,
+					"-%c requires packet and byte counter",
+					opt2char(OPT_COUNTERS));
+
+			if (sscanf(pcnt, "%"PRIu64, &fw.counters.pcnt) != 1)
+				exit_error(PARAMETER_PROBLEM,
+					"-%c packet counter not numeric",
+					opt2char(OPT_COUNTERS));
+
+			if (sscanf(bcnt, "%"PRIu64, &fw.counters.bcnt) != 1)
+				exit_error(PARAMETER_PROBLEM,
+					"-%c byte counter not numeric",
+					opt2char(OPT_COUNTERS));
+			
+			break;
+
+
+		case 1: /* non option */
+			if (optarg[0] == '!' && optarg[1] == '\0') {
+				if (invert)
+					exit_error(PARAMETER_PROBLEM,
+						   "multiple consecutive ! not"
+						   " allowed");
+				invert = TRUE;
+				optarg[0] = '\0';
+				continue;
+			}
+			printf("Bad argument `%s'\n", optarg);
+			exit_tryhelp(2);
+
+		default:
+			/* FIXME: This scheme doesn't allow two of the same
+			   matches --RR */
+			if (!target
+			    || !(target->parse(c - target->option_offset,
+					       argv, invert,
+					       &target->tflags,
+					       &fw, &target->t))) {
+/*
+				for (m = arptables_matches; m; m = m->next) {
+					if (!m->used)
+						continue;
+
+					if (m->parse(c - m->option_offset,
+						     argv, invert,
+						     &m->mflags,
+						     &fw,
+						     &fw.nfcache,
+						     &m->m))
+						break;
+				}
+*/
+
+				/* If you listen carefully, you can
+				   actually hear this code suck. */
+
+				/* some explanations (after four different bugs
+				 * in 3 different releases): If we encountere a
+				 * parameter, that has not been parsed yet,
+				 * it's not an option of an explicitly loaded
+				 * match or a target.  However, we support
+				 * implicit loading of the protocol match
+				 * extension.  '-p tcp' means 'l4 proto 6' and
+				 * at the same time 'load tcp protocol match on
+				 * demand if we specify --dport'.
+				 *
+				 * To make this work, we need to make sure:
+				 * - the parameter has not been parsed by
+				 *   a match (m above)
+				 * - a protocol has been specified
+				 * - the protocol extension has not been
+				 *   loaded yet, or is loaded and unused
+				 *   [think of arptables-restore!]
+				 * - the protocol extension can be successively
+				 *   loaded
+				 */
+/*
+				if (m == NULL
+				    && protocol
+				    && (!find_proto(protocol, DONT_LOAD,
+						   options&OPT_NUMERIC) 
+					|| (find_proto(protocol, DONT_LOAD,
+							options&OPT_NUMERIC)
+					    && (proto_used == 0))
+				       )
+				    && (m = find_proto(protocol, TRY_LOAD,
+						       options&OPT_NUMERIC))) {
+					 Try loading protocol */
+/*
+					size_t size;
+
+					proto_used = 1;
+
+					size = ARPT_ALIGN(sizeof(struct arpt_entry_match))
+							 + m->size;
+
+					m->m = fw_calloc(1, size);
+					m->m->u.match_size = size;
+					strcpy(m->m->u.user.name, m->name);
+					m->init(m->m, &fw.nfcache);
+
+					opts = merge_options(opts,
+					    m->extra_opts, &m->option_offset);
+
+					optind--;
+					continue;
+				}
+				if (!m)
+					exit_error(PARAMETER_PROBLEM,
+						   "Unknown arg `%s'",
+						   argv[optind-1]);
+*/
+			}
+		}
+		invert = FALSE;
+	}
+/*
+	for (m = arptables_matches; m; m = m->next) {
+		if (!m->used)
+			continue;
+
+		m->final_check(m->mflags);
+	}
+*/
+
+	if (target)
+		target->final_check(target->tflags);
+
+	/* Fix me: must put inverse options checking here --MN */
+
+	if (optind < argc)
+		exit_error(PARAMETER_PROBLEM,
+			   "unknown arguments found on commandline");
+	if (!command)
+		exit_error(PARAMETER_PROBLEM, "no command specified");
+	if (invert)
+		exit_error(PARAMETER_PROBLEM,
+			   "nothing appropriate following !");
+
+	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+		if (!(options & OPT_D_IP))
+			dhostnetworkmask = "0.0.0.0/0";
+		if (!(options & OPT_S_IP))
+			shostnetworkmask = "0.0.0.0/0";
+	}
+
+	if (shostnetworkmask)
+		parse_hostnetworkmask(shostnetworkmask, &saddrs,
+				      &(fw.arp.smsk), &nsaddrs);
+
+	if (dhostnetworkmask)
+		parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+				      &(fw.arp.tmsk), &ndaddrs);
+
+	if ((nsaddrs > 1 || ndaddrs > 1) &&
+	    (fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
+		exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+			   " source or destination IP addresses");
+
+	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+		exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+			   "specify a unique address");
+
+	generic_opt_check(command, options);
+
+	if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
+		exit_error(PARAMETER_PROBLEM,
+			   "chain name `%s' too long (must be under %i chars)",
+			   chain, ARPT_FUNCTION_MAXNAMELEN);
+
+	/* only allocate handle if we weren't called with a handle */
+	if (!*handle)
+		*handle = arptc_init(*table);
+
+	if (!*handle) {
+		/* try to insmod the module if arptc_init failed */
+		arptables_insmod("arp_tables", modprobe);
+		*handle = arptc_init(*table);
+	}
+
+	if (!*handle)
+		exit_error(VERSION_PROBLEM,
+			   "can't initialize arptables table `%s': %s",
+			   *table, arptc_strerror(errno));
+
+	if (command == CMD_APPEND
+	    || command == CMD_DELETE
+	    || command == CMD_INSERT
+	    || command == CMD_REPLACE) {
+		if (strcmp(chain, "PREROUTING") == 0
+		    || strcmp(chain, "INPUT") == 0) {
+			/* -o not valid with incoming packets. */
+			if (options & OPT_VIANAMEOUT)
+				exit_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEOUT),
+					   chain);
+		}
+
+		if (strcmp(chain, "POSTROUTING") == 0
+		    || strcmp(chain, "OUTPUT") == 0) {
+			/* -i not valid with outgoing packets */
+			if (options & OPT_VIANAMEIN)
+				exit_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEIN),
+					   chain);
+		}
+
+		if (target && arptc_is_chain(jumpto, *handle)) {
+			printf("Warning: using chain %s, not extension\n",
+			       jumpto);
+
+			target = NULL;
+		}
+
+		/* If they didn't specify a target, or it's a chain
+		   name, use standard. */
+		if (!target
+		    && (strlen(jumpto) == 0
+			|| arptc_is_chain(jumpto, *handle))) {
+			size_t size;
+
+			target = find_target(ARPT_STANDARD_TARGET,
+					     LOAD_MUST_SUCCEED);
+
+			size = sizeof(struct arpt_entry_target)
+				+ target->size;
+			target->t = fw_calloc(1, size);
+			target->t->u.target_size = size;
+			strcpy(target->t->u.user.name, jumpto);
+			target->init(target->t);
+		}
+
+		if (!target) {
+			/* it is no chain, and we can't load a plugin.
+			 * We cannot know if the plugin is corrupt, non
+			 * existant OR if the user just misspelled a
+			 * chain. */
+			find_target(jumpto, LOAD_MUST_SUCCEED);
+		} else {
+			e = generate_entry(&fw, arptables_matches, target->t);
+		}
+	}
+
+	switch (command) {
+	case CMD_APPEND:
+		ret = append_entry(chain, e,
+				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   options&OPT_VERBOSE,
+				   handle);
+		break;
+	case CMD_DELETE:
+		ret = delete_entry(chain, e,
+				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   options&OPT_VERBOSE,
+				   handle);
+		break;
+	case CMD_DELETE_NUM:
+		ret = arptc_delete_num_entry(chain, rulenum - 1, handle);
+		break;
+	case CMD_REPLACE:
+		ret = replace_entry(chain, e, rulenum - 1,
+				    saddrs, daddrs, options&OPT_VERBOSE,
+				    handle);
+		break;
+	case CMD_INSERT:
+		ret = insert_entry(chain, e, rulenum - 1,
+				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   options&OPT_VERBOSE,
+				   handle);
+		break;
+	case CMD_LIST:
+		ret = list_entries(chain,
+				   options&OPT_VERBOSE,
+				   options&OPT_NUMERIC,
+				   /*options&OPT_EXPANDED*/0,
+				   options&OPT_LINENUMBERS,
+				   handle);
+		break;
+	case CMD_FLUSH:
+		ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+		break;
+	case CMD_ZERO:
+		ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+		break;
+	case CMD_LIST|CMD_ZERO:
+		ret = list_entries(chain,
+				   options&OPT_VERBOSE,
+				   options&OPT_NUMERIC,
+				   /*options&OPT_EXPANDED*/0,
+				   options&OPT_LINENUMBERS,
+				   handle);
+		if (ret)
+			ret = zero_entries(chain,
+					   options&OPT_VERBOSE, handle);
+		break;
+	case CMD_NEW_CHAIN:
+		ret = arptc_create_chain(chain, handle);
+		break;
+	case CMD_DELETE_CHAIN:
+		ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+		break;
+	case CMD_RENAME_CHAIN:
+		ret = arptc_rename_chain(chain, newname,	handle);
+		break;
+	case CMD_SET_POLICY:
+		ret = arptc_set_policy(chain, policy, NULL, handle);
+		break;
+	default:
+		/* We should never reach this... */
+		exit_tryhelp(2);
+	}
+
+	if (verbose > 1)
+		dump_entries(*handle);
+
+	return ret;
+}
+
diff --git a/userspace/arptables/arptables.sysv b/userspace/arptables/arptables.sysv
new file mode 100644
index 0000000..ea5cf09
--- /dev/null
+++ b/userspace/arptables/arptables.sysv
@@ -0,0 +1,103 @@
+#!/bin/bash
+#
+# init script for arptables
+#
+# Original by Dag Wieers <dag@wieers.com>.
+# Modified/changed to arptables by
+# 	Rok Papez <rok.papez@arnes.si>.
+#
+# chkconfig: - 16 84
+# description: Arp filtering tables
+#
+# config: __SYSCONFIG__/arptables
+
+source /etc/init.d/functions
+source /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x __EXEC_PATH__/arptables ] || exit 1
+[ -x __EXEC_PATH__/arptables-save ] || exit 1
+[ -x __EXEC_PATH__/arptables-restore ] || exit 1
+
+[ "$1" != "save" -o -r __SYSCONFIG__/arptables ] || exit 1
+
+RETVAL=0
+prog="arptables"
+desc="Arp filtering"
+
+start() {
+	echo -n $"Starting $desc ($prog): "
+	__EXEC_PATH__/arptables-restore < __SYSCONFIG__/arptables || RETVAL=1
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog startup"
+		rm -f /var/lock/subsys/$prog
+	else
+		failure "$prog startup"
+	fi
+
+	echo
+	return $RETVAL
+}
+
+stop() {
+	echo -n $"Stopping $desc ($prog): "
+	__EXEC_PATH__/arptables-restore < /dev/null || RETVAL=1
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog shutdown"
+		rm -f %{_localstatedir}/lock/subsys/$prog
+	else
+		failure "$prog shutdown"
+	fi
+
+	echo
+	return $RETVAL
+}
+
+restart() {
+	stop
+	start
+}
+
+save() {
+	echo -n $"Saving $desc ($prog): "
+	__EXEC_PATH__/arptables-save > __SYSCONFIG__/arptables || RETVAL=1
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog saved"
+	else
+		failure "$prog saved"
+	fi
+	echo
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  restart|reload)
+	restart
+	;;
+  condrestart)
+	[ -e /var/lock/subsys/$prog ] && restart
+	RETVAL=$?
+	;;
+  save)
+	save
+	;;
+  status)
+	__EXEC_PATH__/arptables-save
+	RETVAL=$?
+	;;
+  *)
+	echo $"Usage $0 {start|stop|restart|condrestart|save|status}"
+	RETVAL=1
+esac
+
+exit $RETVAL
diff --git a/userspace/arptables/extensions/Makefile b/userspace/arptables/extensions/Makefile
new file mode 100644
index 0000000..09b244e
--- /dev/null
+++ b/userspace/arptables/extensions/Makefile
@@ -0,0 +1,7 @@
+#! /usr/bin/make
+
+EXT_FUNC+=standard mangle CLASSIFY
+EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/arpt_$(T).o)
+
+extensions/ebt_%.o: extensions/arpt_%.c include/arptables.h include/arptables_common.h
+	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $<
diff --git a/userspace/arptables/extensions/arpt_CLASSIFY.c b/userspace/arptables/extensions/arpt_CLASSIFY.c
new file mode 100644
index 0000000..cb5770b
--- /dev/null
+++ b/userspace/arptables/extensions/arpt_CLASSIFY.c
@@ -0,0 +1,121 @@
+/*
+ * (C) 2010 by Frederic Leroy <fredo@starox.org>
+ *
+ * arpt_classify.c -- arptables extension to classify arp packet
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <arptables.h>
+#include <linux/netfilter/xt_CLASSIFY.h>
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+static void
+help(void)
+{
+	printf(
+"CLASSIFY target v%s options:\n"
+"--set-class major:minor : set the major and minor class value\n",
+	ARPTABLES_VERSION);
+}
+
+#define CLASSIFY_OPT 1
+
+static struct option opts[] = {
+	{ "set-class"   , required_argument, 0, CLASSIFY_OPT },
+	{0}
+};
+
+static void
+init(struct arpt_entry_target *t)
+{
+	struct xt_classify_target_info *classify = (struct xt_classify_target_info *) t->data;
+	classify->priority = 0;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+	const struct arpt_entry *e,
+	struct arpt_entry_target **t)
+{
+	struct xt_classify_target_info *classify = (struct xt_classify_target_info *)(*t)->data;
+	int i,j;
+
+	switch (c) {
+		case CLASSIFY_OPT:
+			if (sscanf(argv[optind-1], "%x:%x", &i, &j) != 2) {
+				exit_error(PARAMETER_PROBLEM,
+						"Bad class value `%s'", optarg);
+				return 0;
+			}
+			classify->priority = TC_H_MAKE(i<<16, j);
+			if (*flags)
+				exit_error(PARAMETER_PROBLEM,
+						"CLASSIFY: Can't specify --set-class twice");
+			*flags = 1;
+			break;
+		default:
+			return 0;
+	}
+	return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+	if (!flags)
+		exit_error(PARAMETER_PROBLEM, "CLASSIFY: Parameter --set-class is required");
+}
+
+static void print(const struct arpt_arp *ip,
+	const struct arpt_entry_target *target, int numeric)
+{
+	struct xt_classify_target_info *t = (struct xt_classify_target_info *)(target->data);
+
+	printf("--set-class %x:%x ", TC_H_MAJ(t->priority)>>16, TC_H_MIN(t->priority));
+}
+
+static void
+save(const struct arpt_arp *ip, const struct arpt_entry_target *target)
+{
+}
+
+static
+struct arptables_target classify
+= { NULL,
+	"CLASSIFY",
+	ARPTABLES_VERSION,
+	ARPT_ALIGN(sizeof(struct xt_classify_target_info)),
+	ARPT_ALIGN(sizeof(struct xt_classify_target_info)),
+	&help,
+	&init,
+	&parse,
+	&final_check,
+	&print,
+	&save,
+	opts
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&classify);
+}
diff --git a/userspace/arptables/extensions/arpt_mangle.c b/userspace/arptables/extensions/arpt_mangle.c
new file mode 100644
index 0000000..c38fc16
--- /dev/null
+++ b/userspace/arptables/extensions/arpt_mangle.c
@@ -0,0 +1,215 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <arptables.h>
+#include <linux/netfilter_arp/arpt_mangle.h>
+
+static void
+help(void)
+{
+	printf(
+"mangle target v%s options:\n"
+"--mangle-ip-s IP address\n"
+"--mangle-ip-d IP address\n"
+"--mangle-mac-s MAC address\n"
+"--mangle-mac-d MAC address\n"
+"--mangle-target target (DROP, CONTINUE or ACCEPT -- default is ACCEPT)\n",
+	ARPTABLES_VERSION);
+}
+
+#define MANGLE_IPS    '1'
+#define MANGLE_IPT    '2'
+#define MANGLE_DEVS   '3'
+#define MANGLE_DEVT   '4'
+#define MANGLE_TARGET '5'
+static struct option opts[] = {
+	{ "mangle-ip-s"   , required_argument, 0, MANGLE_IPS    },
+	{ "mangle-ip-d"   , required_argument, 0, MANGLE_IPT    },
+	{ "mangle-mac-s"  , required_argument, 0, MANGLE_DEVS   },
+	{ "mangle-mac-d"  , required_argument, 0, MANGLE_DEVT   },
+	{ "mangle-target" , required_argument, 0, MANGLE_TARGET },
+	{0}
+};
+
+static void
+init(struct arpt_entry_target *t)
+{
+	struct arpt_mangle *mangle = (struct arpt_mangle *) t->data;
+
+	mangle->target = NF_ACCEPT;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct arpt_entry *e,
+      struct arpt_entry_target **t)
+{
+	struct arpt_mangle *mangle = (struct arpt_mangle *)(*t)->data;
+	struct in_addr *ipaddr;
+	struct ether_addr *macaddr;
+	int ret = 1;
+
+	switch (c) {
+	case MANGLE_IPS:
+/*
+		if (e->arp.arpln_mask == 0)
+			exit_error(PARAMETER_PROBLEM, "no pln defined");
+
+		if (e->arp.invflags & ARPT_INV_ARPPLN)
+			exit_error(PARAMETER_PROBLEM,
+				   "! pln not allowed for --mangle-ip-s");
+*/
+/*
+		if (e->arp.arpln != 4)
+			exit_error(PARAMETER_PROBLEM, "only pln=4 supported");
+*/
+		{
+			unsigned int nr;
+			ipaddr = parse_hostnetwork(argv[optind-1], &nr);
+		}
+		mangle->u_s.src_ip.s_addr = ipaddr->s_addr;
+		free(ipaddr);
+		mangle->flags |= ARPT_MANGLE_SIP;
+		break;
+	case MANGLE_IPT:
+/*
+		if (e->arp.arpln_mask == 0)
+			exit_error(PARAMETER_PROBLEM, "no pln defined");
+
+		if (e->arp.invflags & ARPT_INV_ARPPLN)
+			exit_error(PARAMETER_PROBLEM,
+				   "! pln not allowed for --mangle-ip-d");
+*/
+/*
+		if (e->arp.arpln != 4)
+			exit_error(PARAMETER_PROBLEM, "only pln=4 supported");
+*/
+		{
+			unsigned int nr;
+			ipaddr = parse_hostnetwork(argv[optind-1], &nr);
+		}
+		mangle->u_t.tgt_ip.s_addr = ipaddr->s_addr;
+		free(ipaddr);
+		mangle->flags |= ARPT_MANGLE_TIP;
+		break;
+	case MANGLE_DEVS:
+		if (e->arp.arhln_mask == 0)
+			exit_error(PARAMETER_PROBLEM, "no --h-length defined");
+		if (e->arp.invflags & ARPT_INV_ARPHLN)
+			exit_error(PARAMETER_PROBLEM,
+				   "! --h-length not allowed for "
+				   "--mangle-mac-s");
+		if (e->arp.arhln != 6)
+			exit_error(PARAMETER_PROBLEM, "only --h-length 6 "
+						      "supported");
+		macaddr = ether_aton(argv[optind-1]);
+		if (macaddr == NULL)
+			exit_error(PARAMETER_PROBLEM, "invalid source MAC");
+		memcpy(mangle->src_devaddr, macaddr, e->arp.arhln);
+		mangle->flags |= ARPT_MANGLE_SDEV;
+		break;
+	case MANGLE_DEVT:
+		if (e->arp.arhln_mask == 0)
+			exit_error(PARAMETER_PROBLEM, "no --h-length defined");
+		if (e->arp.invflags & ARPT_INV_ARPHLN)
+			exit_error(PARAMETER_PROBLEM,
+				   "! hln not allowed for --mangle-mac-d");
+		if (e->arp.arhln != 6)
+			exit_error(PARAMETER_PROBLEM, "only --h-length 6 "
+						      "supported");
+		macaddr = ether_aton(argv[optind-1]);
+		if (macaddr == NULL)
+			exit_error(PARAMETER_PROBLEM, "invalid target MAC");
+		memcpy(mangle->tgt_devaddr, macaddr, e->arp.arhln);
+		mangle->flags |= ARPT_MANGLE_TDEV;
+		break;
+	case MANGLE_TARGET:
+		if (!strcmp(argv[optind-1], "DROP"))
+			mangle->target = NF_DROP;
+		else if (!strcmp(argv[optind-1], "ACCEPT"))
+			mangle->target = NF_ACCEPT;
+		else if (!strcmp(argv[optind-1], "CONTINUE"))
+			mangle->target = ARPT_CONTINUE;
+		else
+			exit_error(PARAMETER_PROBLEM, "bad target for "
+						      "--mangle-target");
+		break;
+	default:
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void final_check(unsigned int flags)
+{
+}
+
+static void print(const struct arpt_arp *ip,
+   const struct arpt_entry_target *target, int numeric)
+{
+	struct arpt_mangle *m = (struct arpt_mangle *)(target->data);
+	char buf[100];
+
+	if (m->flags & ARPT_MANGLE_SIP) {
+		if (numeric)
+			sprintf(buf, "%s", addr_to_dotted(&(m->u_s.src_ip)));
+		else
+			sprintf(buf, "%s", addr_to_anyname(&(m->u_s.src_ip)));
+		printf("--mangle-ip-s %s ", buf);
+	}
+	if (m->flags & ARPT_MANGLE_SDEV) {
+		printf("--mangle-mac-s ");
+		print_mac((unsigned char *)m->src_devaddr, 6);
+		printf(" ");
+	}
+	if (m->flags & ARPT_MANGLE_TIP) {
+		if (numeric)
+			sprintf(buf, "%s", addr_to_dotted(&(m->u_t.tgt_ip)));
+		else
+			sprintf(buf, "%s", addr_to_anyname(&(m->u_t.tgt_ip)));
+		printf("--mangle-ip-d %s ", buf);
+	}
+	if (m->flags & ARPT_MANGLE_TDEV) {
+		printf("--mangle-mac-d ");
+		print_mac((unsigned char *)m->tgt_devaddr, 6);
+		printf(" ");
+	}
+	if (m->target != NF_ACCEPT) {
+		printf("--mangle-target ");
+		if (m->target == NF_DROP)
+			printf("DROP ");
+		else
+			printf("CONTINUE ");
+	}
+}
+
+static void
+save(const struct arpt_arp *ip, const struct arpt_entry_target *target)
+{
+}
+
+static
+struct arptables_target change
+= { NULL,
+    "mangle",
+    ARPTABLES_VERSION,
+    ARPT_ALIGN(sizeof(struct arpt_mangle)),
+    ARPT_ALIGN(sizeof(struct arpt_mangle)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&change);
+}
diff --git a/userspace/arptables/extensions/arpt_standard.c b/userspace/arptables/extensions/arpt_standard.c
new file mode 100644
index 0000000..cb3891d
--- /dev/null
+++ b/userspace/arptables/extensions/arpt_standard.c
@@ -0,0 +1,70 @@
+/* Shared library add-on to arptables for standard target support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <arptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+	printf(
+"Standard v%s options:\n"
+"(If target is DROP, ACCEPT, RETURN or nothing)\n", ARPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+	{0}
+};
+
+/* Initialize the target. */
+static void
+init(struct arpt_entry_target *t)
+{
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct arpt_entry *entry,
+      struct arpt_entry_target **target)
+{
+	return 0;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct arpt_arp *ip, const struct arpt_entry_target *target)
+{
+}
+
+static
+struct arptables_target standard
+= { NULL,
+    "standard",
+    ARPTABLES_VERSION,
+    ARPT_ALIGN(sizeof(int)),
+    ARPT_ALIGN(sizeof(int)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    NULL, /* print */
+    &save,
+    opts
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&standard);
+}
diff --git a/userspace/arptables/include/arp_tables.h b/userspace/arptables/include/arp_tables.h
new file mode 100644
index 0000000..2861cfd
--- /dev/null
+++ b/userspace/arptables/include/arp_tables.h
@@ -0,0 +1,342 @@
+/*
+ * 	Format of an ARP firewall descriptor
+ *
+ * 	src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in
+ *	network byte order.
+ * 	flags are stored in host byte order (of course).
+ */
+
+#ifndef _ARPTABLES_H
+#define _ARPTABLES_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#endif
+
+#include <linux/netfilter_arp.h>
+
+#define ARPT_FUNCTION_MAXNAMELEN 30
+#define ARPT_TABLE_MAXNAMELEN 32
+
+#define ARPT_DEV_ADDR_LEN_MAX 16
+
+struct arpt_devaddr_info {
+	char addr[ARPT_DEV_ADDR_LEN_MAX];
+	char mask[ARPT_DEV_ADDR_LEN_MAX];
+};
+
+/* Yes, Virginia, you have to zero the padding. */
+struct arpt_arp {
+	/* Source and target IP addr */
+	struct in_addr src, tgt;
+	/* Mask for src and target IP addr */
+	struct in_addr smsk, tmsk;
+
+	/* Device hw address length, src+target device addresses */
+	u_int8_t arhln, arhln_mask;
+	struct arpt_devaddr_info src_devaddr;
+	struct arpt_devaddr_info tgt_devaddr;
+
+	/* ARP operation code. */
+	u_int16_t arpop, arpop_mask;
+
+	/* ARP hardware address and protocol address format. */
+	u_int16_t arhrd, arhrd_mask;
+	u_int16_t arpro, arpro_mask;
+
+	/* The protocol address length is only accepted if it is 4
+	 * so there is no use in offering a way to do filtering on it.
+	 */
+
+	char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+	/* Flags word */
+	u_int8_t flags;
+	/* Inverse flags */
+	u_int16_t invflags;
+};
+
+struct arpt_entry_target
+{
+	union {
+		struct {
+			u_int16_t target_size;
+
+			/* Used by userspace */
+			char name[ARPT_FUNCTION_MAXNAMELEN];
+		} user;
+		struct {
+			u_int16_t target_size;
+
+			/* Used inside the kernel */
+			struct arpt_target *target;
+		} kernel;
+
+		/* Total length */
+		u_int16_t target_size;
+	} u;
+
+	unsigned char data[0];
+};
+
+struct arpt_standard_target
+{
+	struct arpt_entry_target target;
+	int verdict;
+};
+
+struct arpt_counters
+{
+	u_int64_t pcnt, bcnt;			/* Packet and byte counters */
+};
+
+/* Values for "flag" field in struct arpt_ip (general arp structure).
+ * No flags defined yet.
+ */
+#define ARPT_F_MASK		0x00	/* All possible flag bits mask. */
+
+/* Values for "inv" field in struct arpt_arp. */
+#define ARPT_INV_VIA_IN		0x0001	/* Invert the sense of IN IFACE. */
+#define ARPT_INV_VIA_OUT	0x0002	/* Invert the sense of OUT IFACE */
+#define ARPT_INV_SRCIP		0x0004	/* Invert the sense of SRC IP. */
+#define ARPT_INV_TGTIP		0x0008	/* Invert the sense of TGT IP. */
+#define ARPT_INV_SRCDEVADDR	0x0010	/* Invert the sense of SRC DEV ADDR. */
+#define ARPT_INV_TGTDEVADDR	0x0020	/* Invert the sense of TGT DEV ADDR. */
+#define ARPT_INV_ARPOP		0x0040	/* Invert the sense of ARP OP. */
+#define ARPT_INV_ARPHRD		0x0080	/* Invert the sense of ARP HRD. */
+#define ARPT_INV_ARPPRO		0x0100	/* Invert the sense of ARP PRO. */
+#define ARPT_INV_ARPHLN		0x0200	/* Invert the sense of ARP HLN. */
+#define ARPT_INV_MASK		0x03FF	/* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules.  Consists of 3
+   parts which are 1) general ARP header stuff 2) match specific
+   stuff 3) the target to perform if the rule matches */
+struct arpt_entry
+{
+	struct arpt_arp arp;
+
+	/* Size of arpt_entry + matches */
+	u_int16_t target_offset;
+	/* Size of arpt_entry + matches + target */
+	u_int16_t next_offset;
+
+	/* Back pointer */
+	unsigned int comefrom;
+
+	/* Packet and byte counters. */
+	struct arpt_counters counters;
+
+	/* The matches (if any), then the target. */
+	unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls.
+ */
+#define ARPT_BASE_CTL		96	/* base for firewall socket options */
+
+#define ARPT_SO_SET_REPLACE		(ARPT_BASE_CTL)
+#define ARPT_SO_SET_ADD_COUNTERS	(ARPT_BASE_CTL + 1)
+#define ARPT_SO_SET_MAX			ARPT_SO_SET_ADD_COUNTERS
+
+#define ARPT_SO_GET_INFO		(ARPT_BASE_CTL)
+#define ARPT_SO_GET_ENTRIES		(ARPT_BASE_CTL + 1)
+#define ARPT_SO_GET_MAX			ARPT_SO_GET_ENTRIES
+
+/* CONTINUE verdict for targets */
+#define ARPT_CONTINUE 0xFFFFFFFF
+
+/* For standard target */
+#define ARPT_RETURN (-NF_MAX_VERDICT - 1)
+
+/* The argument to ARPT_SO_GET_INFO */
+struct arpt_getinfo
+{
+	/* Which table: caller fills this in. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Kernel fills these in. */
+	/* Which hook entry points are valid: bitmask */
+	unsigned int valid_hooks;
+
+	/* Hook entry points: one per netfilter hook. */
+	unsigned int hook_entry[3];
+
+	/* Underflow points. */
+	unsigned int underflow[3];
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Size of entries. */
+	unsigned int size;
+};
+
+/* The argument to ARPT_SO_SET_REPLACE. */
+struct arpt_replace
+{
+	/* Which table. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Which hook entry points are valid: bitmask.  You can't
+           change this. */
+	unsigned int valid_hooks;
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Total size of new entries */
+	unsigned int size;
+
+	/* Hook entry points. */
+	unsigned int hook_entry[3];
+
+	/* Underflow points. */
+	unsigned int underflow[3];
+
+	/* Information about old entries: */
+	/* Number of counters (must be equal to current number of entries). */
+	unsigned int num_counters;
+	/* The old entries' counters. */
+	struct arpt_counters *counters;
+
+	/* The entries (hang off end: not really an array). */
+	struct arpt_entry entries[0];
+};
+
+/* The argument to ARPT_SO_ADD_COUNTERS. */
+struct arpt_counters_info
+{
+	/* Which table. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	unsigned int num_counters;
+
+	/* The counters (actually `number' of these). */
+	struct arpt_counters counters[0];
+};
+
+/* The argument to ARPT_SO_GET_ENTRIES. */
+struct arpt_get_entries
+{
+	/* Which table: user fills this in. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* User fills this in: total entry size. */
+	unsigned int size;
+
+	/* The entries. */
+	struct arpt_entry entrytable[0];
+};
+
+/* Standard return verdict, or do jump. */
+#define ARPT_STANDARD_TARGET ""
+/* Error verdict. */
+#define ARPT_ERROR_TARGET "ERROR"
+
+/* Helper functions */
+static __inline__ struct arpt_entry_target *arpt_get_target(struct arpt_entry *e)
+{
+	return (void *)e + e->target_offset;
+}
+
+/* fn returns 0 to continue iteration */
+#define ARPT_ENTRY_ITERATE(entries, size, fn, args...)		\
+({								\
+	unsigned int __i;					\
+	int __ret = 0;						\
+	struct arpt_entry *__entry;				\
+								\
+	for (__i = 0; __i < (size); __i += __entry->next_offset) { \
+		__entry = (void *)(entries) + __i;		\
+								\
+		__ret = fn(__entry , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/*
+ *	Main firewall chains definitions and global var's definitions.
+ */
+#ifdef __KERNEL__
+
+/* Registration hooks for targets. */
+struct arpt_target
+{
+	struct list_head list;
+
+	const char name[ARPT_FUNCTION_MAXNAMELEN];
+
+	/* Returns verdict. */
+	unsigned int (*target)(struct sk_buff **pskb,
+			       unsigned int hooknum,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       const void *targinfo,
+			       void *userdata);
+
+	/* Called when user tries to insert an entry of this type:
+           hook_mask is a bitmask of hooks from which it can be
+           called. */
+	/* Should return true or false. */
+	int (*checkentry)(const char *tablename,
+			  const struct arpt_entry *e,
+			  void *targinfo,
+			  unsigned int targinfosize,
+			  unsigned int hook_mask);
+
+	/* Called when entry of this type deleted. */
+	void (*destroy)(void *targinfo, unsigned int targinfosize);
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
+};
+
+extern int arpt_register_target(struct arpt_target *target);
+extern void arpt_unregister_target(struct arpt_target *target);
+
+/* Furniture shopping... */
+struct arpt_table
+{
+	struct list_head list;
+
+	/* A unique name... */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Seed table: copied in register_table */
+	struct arpt_replace *table;
+
+	/* What hooks you will enter on */
+	unsigned int valid_hooks;
+
+	/* Lock for the curtain */
+	rwlock_t lock;
+
+	/* Man behind the curtain... */
+	struct arpt_table_info *private;
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
+};
+
+extern int arpt_register_table(struct arpt_table *table);
+extern void arpt_unregister_table(struct arpt_table *table);
+extern unsigned int arpt_do_table(struct sk_buff **pskb,
+				  unsigned int hook,
+				  const struct net_device *in,
+				  const struct net_device *out,
+				  struct arpt_table *table,
+				  void *userdata);
+
+#define ARPT_ALIGN(s) (((s) + (__alignof__(struct arpt_entry)-1)) & ~(__alignof__(struct arpt_entry)-1))
+#endif /*__KERNEL__*/
+#endif /* _ARPTABLES_H */
diff --git a/userspace/arptables/include/arptables.h b/userspace/arptables/include/arptables.h
new file mode 100644
index 0000000..820b664
--- /dev/null
+++ b/userspace/arptables/include/arptables.h
@@ -0,0 +1,154 @@
+#ifndef _ARPTABLES_USER_H
+#define _ARPTABLES_USER_H
+
+#include "arptables_common.h"
+#include "libarptc/libarptc.h"
+
+
+/*******************************/
+/* REMOVE LATER, PUT IN KERNEL */
+/*******************************/
+struct arpt_entry_match
+{
+	int iets;
+};
+
+/*******************************/
+/* END OF KERNEL REPLACEMENTS  */
+/*******************************/
+
+/* Include file for additions: new matches and targets. */
+struct arptables_match
+{
+	struct arptables_match *next;
+
+	arpt_chainlabel name;
+
+	const char *version;
+
+	/* Size of match data. */
+	size_t size;
+
+	/* Size of match data relevent for userspace comparison purposes */
+	size_t userspacesize;
+
+	/* Function which prints out usage message. */
+	void (*help)(void);
+
+	/* Initialize the match. */
+	void (*init)(struct arpt_entry_match *m, unsigned int *nfcache);
+
+	/* Function which parses command options; returns true if it
+           ate an option */
+	int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+		     const struct arpt_entry *entry,
+		     unsigned int *nfcache,
+		     struct arpt_entry_match **match);
+
+	/* Final check; exit if not ok. */
+	void (*final_check)(unsigned int flags);
+
+	/* Prints out the match iff non-NULL: put space at end */
+	void (*print)(const struct arpt_arp *ip,
+		      const struct arpt_entry_match *match, int numeric);
+
+	/* Saves the match info in parsable form to stdout. */
+	void (*save)(const struct arpt_arp *ip,
+		     const struct arpt_entry_match *match);
+
+	/* Pointer to list of extra command-line options */
+	const struct option *extra_opts;
+
+	/* Ignore these men behind the curtain: */
+	unsigned int option_offset;
+	struct arpt_entry_match *m;
+	unsigned int mflags;
+	unsigned int used;
+	unsigned int loaded; /* simulate loading so options are merged properly */
+};
+
+struct arptables_target
+{
+	struct arptables_target *next;
+
+	arpt_chainlabel name;
+
+	const char *version;
+
+	/* Size of target data. */
+	size_t size;
+
+	/* Size of target data relevent for userspace comparison purposes */
+	size_t userspacesize;
+
+	/* Function which prints out usage message. */
+	void (*help)(void);
+
+	/* Initialize the target. */
+	void (*init)(struct arpt_entry_target *t);
+
+	/* Function which parses command options; returns true if it
+           ate an option */
+	int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+		     const struct arpt_entry *entry,
+		     struct arpt_entry_target **target);
+
+	/* Final check; exit if not ok. */
+	void (*final_check)(unsigned int flags);
+
+	/* Prints out the target iff non-NULL: put space at end */
+	void (*print)(const struct arpt_arp *ip,
+		      const struct arpt_entry_target *target, int numeric);
+
+	/* Saves the targinfo in parsable form to stdout. */
+	void (*save)(const struct arpt_arp *ip,
+		     const struct arpt_entry_target *target);
+
+	/* Pointer to list of extra command-line options */
+	struct option *extra_opts;
+
+	/* Ignore these men behind the curtain: */
+	unsigned int option_offset;
+	struct arpt_entry_target *t;
+	unsigned int tflags;
+	unsigned int used;
+	unsigned int loaded; /* simulate loading so options are merged properly */
+};
+
+/* Your shared library should call one of these. */
+extern void register_match(struct arptables_match *me);
+extern void register_target(struct arptables_target *me);
+
+extern struct in_addr *dotted_to_addr(const char *dotted);
+extern char *addr_to_dotted(const struct in_addr *addrp);
+extern char *addr_to_anyname(const struct in_addr *addr);
+extern char *mask_to_dotted(const struct in_addr *mask);
+
+extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                      struct in_addr *maskp, unsigned int *naddrs);
+extern u_int16_t parse_protocol(const char *s);
+
+extern int do_command(int argc, char *argv[], char **table,
+		      arptc_handle_t *handle);
+/* Keeping track of external matches and targets: linked lists.  */
+extern struct arptables_match *arptables_matches;
+extern struct arptables_target *arptables_targets;
+
+enum arpt_tryload {
+	DONT_LOAD,
+	TRY_LOAD,
+	LOAD_MUST_SUCCEED
+};
+
+extern struct arptables_target *find_target(const char *name, enum arpt_tryload);
+extern struct arptables_match *find_match(const char *name, enum arpt_tryload);
+
+extern int delete_chain(const arpt_chainlabel chain, int verbose,
+			arptc_handle_t *handle);
+extern int flush_entries(const arpt_chainlabel chain, int verbose, 
+			arptc_handle_t *handle);
+extern int for_each_chain(int (*fn)(const arpt_chainlabel, int, arptc_handle_t *),
+		int verbose, int builtinstoo, arptc_handle_t *handle);
+struct in_addr *parse_hostnetwork(const char *name, unsigned int *naddrs);
+void print_mac(const unsigned char *mac, int l);
+#endif /*_ARPTABLES_USER_H*/
diff --git a/userspace/arptables/include/arptables_common.h b/userspace/arptables/include/arptables_common.h
new file mode 100644
index 0000000..8150ee0
--- /dev/null
+++ b/userspace/arptables/include/arptables_common.h
@@ -0,0 +1,23 @@
+#ifndef _ARPTABLES_COMMON_H
+#define _ARPTABLES_COMMON_H
+
+enum exittype {
+	OTHER_PROBLEM = 1,
+	PARAMETER_PROBLEM,
+	VERSION_PROBLEM
+};
+extern void exit_printhelp() __attribute__((noreturn));
+extern void exit_tryhelp(int) __attribute__((noreturn));
+int check_inverse(const char option[], int *invert, int *optind, int argc);
+extern int string_to_number(const char *, 
+			    unsigned int, 
+			    unsigned int,
+			    unsigned int *);
+extern int iptables_insmod(const char *modname, const char *modprobe);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+							  format(printf,2,3)));
+extern const char *program_name, *program_version;
+
+  extern void init_extensions(void);
+
+#endif /*_IPTABLES_COMMON_H*/
diff --git a/userspace/arptables/include/libarptc/arpt_kernel_headers.h b/userspace/arptables/include/libarptc/arpt_kernel_headers.h
new file mode 100644
index 0000000..442cc54
--- /dev/null
+++ b/userspace/arptables/include/libarptc/arpt_kernel_headers.h
@@ -0,0 +1,29 @@
+/* This is the userspace/kernel interface for Generic IP Chains,
+   required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#include <limits.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ether.h>
+#include <net/if.h>
+#include <sys/types.h>
+#else
+#include <sys/socket.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/in6.h>
+#endif
+
+#endif
diff --git a/userspace/arptables/include/libarptc/libarptc.h b/userspace/arptables/include/libarptc/libarptc.h
new file mode 100644
index 0000000..e4f1175
--- /dev/null
+++ b/userspace/arptables/include/libarptc/libarptc.h
@@ -0,0 +1,161 @@
+#ifndef _LIBARPTC_H
+#define _LIBARPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <libarptc/arpt_kernel_headers.h>
+#include <linux/netfilter_arp/arp_tables.h>
+
+#ifndef ARPT_MIN_ALIGN
+/* arpt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define ARPT_MIN_ALIGN (__alignof__(struct arpt_entry))
+#endif
+
+#define ARPT_ALIGN(s) (((s) + ((ARPT_MIN_ALIGN)-1)) & ~((ARPT_MIN_ALIGN)-1))
+
+typedef char arpt_chainlabel[32];
+
+#define ARPTC_LABEL_ACCEPT  "ACCEPT"
+#define ARPTC_LABEL_DROP    "DROP"
+#define ARPTC_LABEL_QUEUE   "QUEUE"
+#define ARPTC_LABEL_RETURN  "RETURN"
+
+
+/* NF_ARP_NUMHOOKS is different on 2.4 and 2.6; hack to support both */
+extern int RUNTIME_NF_ARP_NUMHOOKS; /* boy, this is dirty */
+
+
+/* Transparent handle type. */
+typedef struct arptc_handle *arptc_handle_t;
+
+/* Does this chain exist? */
+int arptc_is_chain(const char *chain, const arptc_handle_t handle);
+
+/* Take a snapshot of the rules.  Returns NULL on error. */
+arptc_handle_t arptc_init(const char *tablename);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *arptc_first_chain(arptc_handle_t *handle);
+const char *arptc_next_chain(arptc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct arpt_entry *arptc_first_rule(const char *chain,
+					arptc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct arpt_entry *arptc_next_rule(const struct arpt_entry *prev,
+				       arptc_handle_t *handle);
+
+/* Returns a pointer to the target name of this entry. */
+const char *arptc_get_target(const struct arpt_entry *e,
+			    arptc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int arptc_builtin(const char *chain, const arptc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *arptc_get_policy(const char *chain,
+			    struct arpt_counters *counter,
+			    arptc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno.  If errno ==
+   0, it means there was a version error (ie. upgrade libarptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int arptc_insert_entry(const arpt_chainlabel chain,
+		      const struct arpt_entry *e,
+		      unsigned int rulenum,
+		      arptc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int arptc_replace_entry(const arpt_chainlabel chain,
+		       const struct arpt_entry *e,
+		       unsigned int rulenum,
+		       arptc_handle_t *handle);
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int arptc_append_entry(const arpt_chainlabel chain,
+		      const struct arpt_entry *e,
+		      arptc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `e', subject to
+   matchmask (array of length == origfw) */
+int arptc_delete_entry(const arpt_chainlabel chain,
+		      const struct arpt_entry *origfw,
+		      unsigned char *matchmask,
+		      arptc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int arptc_delete_num_entry(const arpt_chainlabel chain,
+			  unsigned int rulenum,
+			  arptc_handle_t *handle);
+
+/* Check the packet `e' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *arptc_check_packet(const arpt_chainlabel chain,
+			      struct arpt_entry *entry,
+			      arptc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int arptc_flush_entries(const arpt_chainlabel chain,
+		       arptc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int arptc_zero_entries(const arpt_chainlabel chain,
+		      arptc_handle_t *handle);
+
+/* Creates a new chain. */
+int arptc_create_chain(const arpt_chainlabel chain,
+		      arptc_handle_t *handle);
+
+/* Deletes a chain. */
+int arptc_delete_chain(const arpt_chainlabel chain,
+		      arptc_handle_t *handle);
+
+/* Renames a chain. */
+int arptc_rename_chain(const arpt_chainlabel oldname,
+		      const arpt_chainlabel newname,
+		      arptc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int arptc_set_policy(const arpt_chainlabel chain,
+		    const arpt_chainlabel policy,
+		    struct arpt_counters *counters,
+		    arptc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int arptc_get_references(unsigned int *ref,
+			const arpt_chainlabel chain,
+			arptc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct arpt_counters *arptc_read_counter(const arpt_chainlabel chain,
+				       unsigned int rulenum,
+				       arptc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int arptc_zero_counter(const arpt_chainlabel chain,
+		      unsigned int rulenum,
+		      arptc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int arptc_set_counter(const arpt_chainlabel chain,
+		     unsigned int rulenum,
+		     struct arpt_counters *counters,
+		     arptc_handle_t *handle);
+
+/* Makes the actual changes. */
+int arptc_commit(arptc_handle_t *handle);
+
+/* Get raw socket. */
+int arptc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *arptc_strerror(int err);
+
+
+
+#endif /* _LIBARPTC_H */
diff --git a/userspace/arptables/include/linux/netfilter_arp.h b/userspace/arptables/include/linux/netfilter_arp.h
new file mode 100644
index 0000000..92bc6dd
--- /dev/null
+++ b/userspace/arptables/include/linux/netfilter_arp.h
@@ -0,0 +1,19 @@
+#ifndef __LINUX_ARP_NETFILTER_H
+#define __LINUX_ARP_NETFILTER_H
+
+/* ARP-specific defines for netfilter.
+ * (C)2002 Rusty Russell IBM -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* There is no PF_ARP. */
+#define NF_ARP		0
+
+/* ARP Hooks */
+#define NF_ARP_IN	0
+#define NF_ARP_OUT	1
+#define NF_ARP_FORWARD	2
+#define NF_ARP_NUMHOOKS	3
+
+#endif /* __LINUX_ARP_NETFILTER_H */
diff --git a/userspace/arptables/include/linux/netfilter_arp/arp_tables.h b/userspace/arptables/include/linux/netfilter_arp/arp_tables.h
new file mode 100644
index 0000000..0acda66
--- /dev/null
+++ b/userspace/arptables/include/linux/netfilter_arp/arp_tables.h
@@ -0,0 +1,342 @@
+/*
+ * 	Format of an ARP firewall descriptor
+ *
+ * 	src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in
+ *	network byte order.
+ * 	flags are stored in host byte order (of course).
+ */
+
+#ifndef _ARPTABLES_H
+#define _ARPTABLES_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#endif
+
+#include <linux/netfilter_arp.h>
+
+#define ARPT_FUNCTION_MAXNAMELEN 30
+#define ARPT_TABLE_MAXNAMELEN 32
+
+#define ARPT_DEV_ADDR_LEN_MAX 16
+
+struct arpt_devaddr_info {
+	char addr[ARPT_DEV_ADDR_LEN_MAX];
+	char mask[ARPT_DEV_ADDR_LEN_MAX];
+};
+
+/* Yes, Virginia, you have to zero the padding. */
+struct arpt_arp {
+	/* Source and target IP addr */
+	struct in_addr src, tgt;
+	/* Mask for src and target IP addr */
+	struct in_addr smsk, tmsk;
+
+	/* Device hw address length, src+target device addresses */
+	u_int8_t arhln, arhln_mask;
+	struct arpt_devaddr_info src_devaddr;
+	struct arpt_devaddr_info tgt_devaddr;
+
+	/* ARP operation code. */
+	u_int16_t arpop, arpop_mask;
+
+	/* ARP hardware address and protocol address format. */
+	u_int16_t arhrd, arhrd_mask;
+	u_int16_t arpro, arpro_mask;
+
+	/* The protocol address length is only accepted if it is 4
+	 * so there is no use in offering a way to do filtering on it.
+	 */
+
+	char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+	/* Flags word */
+	u_int8_t flags;
+	/* Inverse flags */
+	u_int16_t invflags;
+};
+
+struct arpt_entry_target
+{
+	union {
+		struct {
+			u_int16_t target_size;
+
+			/* Used by userspace */
+			char name[ARPT_FUNCTION_MAXNAMELEN];
+		} user;
+		struct {
+			u_int16_t target_size;
+
+			/* Used inside the kernel */
+			struct arpt_target *target;
+		} kernel;
+
+		/* Total length */
+		u_int16_t target_size;
+	} u;
+
+	unsigned char data[0];
+};
+
+struct arpt_standard_target
+{
+	struct arpt_entry_target target;
+	int verdict;
+};
+
+struct arpt_counters
+{
+	u_int64_t pcnt, bcnt;			/* Packet and byte counters */
+};
+
+/* Values for "flag" field in struct arpt_ip (general arp structure).
+ * No flags defined yet.
+ */
+#define ARPT_F_MASK		0x00	/* All possible flag bits mask. */
+
+/* Values for "inv" field in struct arpt_arp. */
+#define ARPT_INV_VIA_IN		0x0001	/* Invert the sense of IN IFACE. */
+#define ARPT_INV_VIA_OUT	0x0002	/* Invert the sense of OUT IFACE */
+#define ARPT_INV_SRCIP		0x0004	/* Invert the sense of SRC IP. */
+#define ARPT_INV_TGTIP		0x0008	/* Invert the sense of TGT IP. */
+#define ARPT_INV_SRCDEVADDR	0x0010	/* Invert the sense of SRC DEV ADDR. */
+#define ARPT_INV_TGTDEVADDR	0x0020	/* Invert the sense of TGT DEV ADDR. */
+#define ARPT_INV_ARPOP		0x0040	/* Invert the sense of ARP OP. */
+#define ARPT_INV_ARPHRD		0x0080	/* Invert the sense of ARP HRD. */
+#define ARPT_INV_ARPPRO		0x0100	/* Invert the sense of ARP PRO. */
+#define ARPT_INV_ARPHLN		0x0200	/* Invert the sense of ARP HLN. */
+#define ARPT_INV_MASK		0x03FF	/* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules.  Consists of 3
+   parts which are 1) general ARP header stuff 2) match specific
+   stuff 3) the target to perform if the rule matches */
+struct arpt_entry
+{
+	struct arpt_arp arp;
+
+	/* Size of arpt_entry + matches */
+	u_int16_t target_offset;
+	/* Size of arpt_entry + matches + target */
+	u_int16_t next_offset;
+
+	/* Back pointer */
+	unsigned int comefrom;
+
+	/* Packet and byte counters. */
+	struct arpt_counters counters;
+
+	/* The matches (if any), then the target. */
+	unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls.
+ */
+#define ARPT_BASE_CTL		96	/* base for firewall socket options */
+
+#define ARPT_SO_SET_REPLACE		(ARPT_BASE_CTL)
+#define ARPT_SO_SET_ADD_COUNTERS	(ARPT_BASE_CTL + 1)
+#define ARPT_SO_SET_MAX			ARPT_SO_SET_ADD_COUNTERS
+
+#define ARPT_SO_GET_INFO		(ARPT_BASE_CTL)
+#define ARPT_SO_GET_ENTRIES		(ARPT_BASE_CTL + 1)
+#define ARPT_SO_GET_MAX			ARPT_SO_GET_ENTRIES
+
+/* CONTINUE verdict for targets */
+#define ARPT_CONTINUE 0xFFFFFFFF
+
+/* For standard target */
+#define ARPT_RETURN (-NF_REPEAT - 1)
+
+/* The argument to ARPT_SO_GET_INFO */
+struct arpt_getinfo
+{
+	/* Which table: caller fills this in. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Kernel fills these in. */
+	/* Which hook entry points are valid: bitmask */
+	unsigned int valid_hooks;
+
+	/* Hook entry points: one per netfilter hook. */
+	unsigned int hook_entry[3];
+
+	/* Underflow points. */
+	unsigned int underflow[3];
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Size of entries. */
+	unsigned int size;
+};
+
+/* The argument to ARPT_SO_SET_REPLACE. */
+struct arpt_replace
+{
+	/* Which table. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Which hook entry points are valid: bitmask.  You can't
+           change this. */
+	unsigned int valid_hooks;
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Total size of new entries */
+	unsigned int size;
+
+	/* Hook entry points. */
+	unsigned int hook_entry[3];
+
+	/* Underflow points. */
+	unsigned int underflow[3];
+
+	/* Information about old entries: */
+	/* Number of counters (must be equal to current number of entries). */
+	unsigned int num_counters;
+	/* The old entries' counters. */
+	struct arpt_counters *counters;
+
+	/* The entries (hang off end: not really an array). */
+	struct arpt_entry entries[0];
+};
+
+/* The argument to ARPT_SO_ADD_COUNTERS. */
+struct arpt_counters_info
+{
+	/* Which table. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	unsigned int num_counters;
+
+	/* The counters (actually `number' of these). */
+	struct arpt_counters counters[0];
+};
+
+/* The argument to ARPT_SO_GET_ENTRIES. */
+struct arpt_get_entries
+{
+	/* Which table: user fills this in. */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* User fills this in: total entry size. */
+	unsigned int size;
+
+	/* The entries. */
+	struct arpt_entry entrytable[0];
+};
+
+/* Standard return verdict, or do jump. */
+#define ARPT_STANDARD_TARGET ""
+/* Error verdict. */
+#define ARPT_ERROR_TARGET "ERROR"
+
+/* Helper functions */
+static __inline__ struct arpt_entry_target *arpt_get_target(struct arpt_entry *e)
+{
+	return (void *)e + e->target_offset;
+}
+
+/* fn returns 0 to continue iteration */
+#define ARPT_ENTRY_ITERATE(entries, size, fn, args...)		\
+({								\
+	unsigned int __i;					\
+	int __ret = 0;						\
+	struct arpt_entry *__entry;				\
+								\
+	for (__i = 0; __i < (size); __i += __entry->next_offset) { \
+		__entry = (void *)(entries) + __i;		\
+								\
+		__ret = fn(__entry , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/*
+ *	Main firewall chains definitions and global var's definitions.
+ */
+#ifdef __KERNEL__
+
+/* Registration hooks for targets. */
+struct arpt_target
+{
+	struct list_head list;
+
+	const char name[ARPT_FUNCTION_MAXNAMELEN];
+
+	/* Returns verdict. */
+	unsigned int (*target)(struct sk_buff **pskb,
+			       unsigned int hooknum,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       const void *targinfo,
+			       void *userdata);
+
+	/* Called when user tries to insert an entry of this type:
+           hook_mask is a bitmask of hooks from which it can be
+           called. */
+	/* Should return true or false. */
+	int (*checkentry)(const char *tablename,
+			  const struct arpt_entry *e,
+			  void *targinfo,
+			  unsigned int targinfosize,
+			  unsigned int hook_mask);
+
+	/* Called when entry of this type deleted. */
+	void (*destroy)(void *targinfo, unsigned int targinfosize);
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
+};
+
+extern int arpt_register_target(struct arpt_target *target);
+extern void arpt_unregister_target(struct arpt_target *target);
+
+/* Furniture shopping... */
+struct arpt_table
+{
+	struct list_head list;
+
+	/* A unique name... */
+	char name[ARPT_TABLE_MAXNAMELEN];
+
+	/* Seed table: copied in register_table */
+	struct arpt_replace *table;
+
+	/* What hooks you will enter on */
+	unsigned int valid_hooks;
+
+	/* Lock for the curtain */
+	rwlock_t lock;
+
+	/* Man behind the curtain... */
+	struct arpt_table_info *private;
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
+};
+
+extern int arpt_register_table(struct arpt_table *table);
+extern void arpt_unregister_table(struct arpt_table *table);
+extern unsigned int arpt_do_table(struct sk_buff **pskb,
+				  unsigned int hook,
+				  const struct net_device *in,
+				  const struct net_device *out,
+				  struct arpt_table *table,
+				  void *userdata);
+
+#define ARPT_ALIGN(s) (((s) + (__alignof__(struct arpt_entry)-1)) & ~(__alignof__(struct arpt_entry)-1))
+#endif /*__KERNEL__*/
+#endif /* _ARPTABLES_H */
diff --git a/userspace/arptables/include/linux/netfilter_arp/arpt_mangle.h b/userspace/arptables/include/linux/netfilter_arp/arpt_mangle.h
new file mode 100644
index 0000000..250f502
--- /dev/null
+++ b/userspace/arptables/include/linux/netfilter_arp/arpt_mangle.h
@@ -0,0 +1,26 @@
+#ifndef _ARPT_MANGLE_H
+#define _ARPT_MANGLE_H
+#include <linux/netfilter_arp/arp_tables.h>
+
+#define ARPT_MANGLE_ADDR_LEN_MAX sizeof(struct in_addr)
+struct arpt_mangle
+{
+	char src_devaddr[ARPT_DEV_ADDR_LEN_MAX];
+	char tgt_devaddr[ARPT_DEV_ADDR_LEN_MAX];
+	union {
+		struct in_addr src_ip;
+	} u_s;
+	union {
+		struct in_addr tgt_ip;
+	} u_t;
+	u_int8_t flags;
+	int target;
+};
+
+#define ARPT_MANGLE_SDEV 0x01
+#define ARPT_MANGLE_TDEV 0x02
+#define ARPT_MANGLE_SIP 0x04
+#define ARPT_MANGLE_TIP 0x08
+#define ARPT_MANGLE_MASK 0x0f
+
+#endif /* _ARPT_MANGLE_H */
diff --git a/userspace/arptables/libarptc/libarptc.c b/userspace/arptables/libarptc/libarptc.c
new file mode 100644
index 0000000..2dcaaef
--- /dev/null
+++ b/userspace/arptables/libarptc/libarptc.c
@@ -0,0 +1,523 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libarptc/libarptc.h"
+
+#define IP_VERSION	4
+#define IP_OFFSET	0x1FFF
+
+#define STRUCT_ENTRY_TARGET	struct arpt_entry_target
+#define STRUCT_ENTRY		struct arpt_entry
+#define STRUCT_ENTRY_MATCH	struct arpt_entry_match
+#define STRUCT_GETINFO		struct arpt_getinfo
+#define STRUCT_GET_ENTRIES	struct arpt_get_entries
+#define STRUCT_COUNTERS		struct arpt_counters
+#define STRUCT_COUNTERS_INFO	struct arpt_counters_info
+#define STRUCT_STANDARD_TARGET	struct arpt_standard_target
+#define STRUCT_REPLACE		struct arpt_replace
+
+#define STRUCT_TC_HANDLE	struct arptc_handle
+#define TC_HANDLE_T		arptc_handle_t
+
+#define ENTRY_ITERATE		ARPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN	ARPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN	ARPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET		arpt_get_target
+
+#define ERROR_TARGET		ARPT_ERROR_TARGET
+
+#define ARPT_CHAINLABEL		arpt_chainlabel
+
+#define TC_DUMP_ENTRIES		dump_entries
+#define TC_IS_CHAIN		arptc_is_chain
+#define TC_FIRST_CHAIN		arptc_first_chain
+#define TC_NEXT_CHAIN		arptc_next_chain
+#define TC_FIRST_RULE		arptc_first_rule
+#define TC_NEXT_RULE		arptc_next_rule
+#define TC_GET_TARGET		arptc_get_target
+#define TC_BUILTIN		arptc_builtin
+#define TC_GET_POLICY		arptc_get_policy
+#define TC_INSERT_ENTRY		arptc_insert_entry
+#define TC_REPLACE_ENTRY	arptc_replace_entry
+#define TC_APPEND_ENTRY		arptc_append_entry
+#define TC_DELETE_ENTRY		arptc_delete_entry
+#define TC_DELETE_NUM_ENTRY	arptc_delete_num_entry
+#define TC_CHECK_PACKET		arptc_check_packet
+#define TC_FLUSH_ENTRIES	arptc_flush_entries
+#define TC_ZERO_ENTRIES		arptc_zero_entries
+#define TC_READ_COUNTER		arptc_read_counter
+#define TC_ZERO_COUNTER		arptc_zero_counter
+#define TC_SET_COUNTER		arptc_set_counter
+#define TC_CREATE_CHAIN		arptc_create_chain
+#define TC_GET_REFERENCES	arptc_get_references
+#define TC_DELETE_CHAIN		arptc_delete_chain
+#define TC_RENAME_CHAIN		arptc_rename_chain
+#define TC_SET_POLICY		arptc_set_policy
+#define TC_GET_RAW_SOCKET	arptc_get_raw_socket
+#define TC_INIT			arptc_init
+#define TC_COMMIT		arptc_commit
+#define TC_STRERROR		arptc_strerror
+
+#define TC_AF			AF_INET
+#define TC_IPPROTO		IPPROTO_IP
+
+#define SO_SET_REPLACE		ARPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS	ARPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO		ARPT_SO_GET_INFO
+#define SO_GET_ENTRIES		ARPT_SO_GET_ENTRIES
+#define SO_GET_VERSION		ARPT_SO_GET_VERSION
+
+#define STANDARD_TARGET		ARPT_STANDARD_TARGET
+#define LABEL_RETURN		ARPTC_LABEL_RETURN
+#define LABEL_ACCEPT		ARPTC_LABEL_ACCEPT
+#define LABEL_DROP		ARPTC_LABEL_DROP
+#define LABEL_QUEUE		ARPTC_LABEL_QUEUE
+
+#define ALIGN			ARPT_ALIGN
+#define RETURN			ARPT_RETURN
+
+#include "libarptc_incl.c"
+
+#define IP_PARTS_NATIVE(n)			\
+(unsigned int)((n)>>24)&0xFF,			\
+(unsigned int)((n)>>16)&0xFF,			\
+(unsigned int)((n)>>8)&0xFF,			\
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+	size_t i;
+	STRUCT_ENTRY_TARGET *t;
+
+	printf("Entry %u (%lu):\n", entry2index(handle, e),
+	       entry2offset(handle, e));
+	printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+	       IP_PARTS(e->arp.src.s_addr),IP_PARTS(e->arp.smsk.s_addr));
+	printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+	       IP_PARTS(e->arp.tgt.s_addr),IP_PARTS(e->arp.tmsk.s_addr));
+	printf("Interface: `%s'/", e->arp.iniface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->arp.iniface_mask[i] ? 'X' : '.');
+	printf("to `%s'/", e->arp.outiface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->arp.outiface_mask[i] ? 'X' : '.');
+	printf("Flags: %02X\n", e->arp.flags);
+	printf("Invflags: %02X\n", e->arp.invflags);
+	printf("Counters: %"PRIu64" packets, %"PRIu64" bytes\n",
+	       e->counters.pcnt, e->counters.bcnt);
+/*
+	printf("Cache: %08X ", e->nfcache);
+	if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+	if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+	if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
+	if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
+	if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
+	if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
+	if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
+	if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
+	if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
+	if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
+	if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
+	if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
+	if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
+*/
+	printf("\n");
+
+/*
+	ARPT_MATCH_ITERATE(e, print_match);
+*/
+
+	t = GET_TARGET(e);
+	printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+	if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+		const unsigned char *data = t->data;
+		const int pos = *(const int *)data;
+		if (pos < 0)
+			printf("verdict=%s\n",
+			       pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+			       : pos == -NF_DROP-1 ? "NF_DROP"
+			       : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+			       : pos == RETURN ? "RETURN"
+			       : "UNKNOWN");
+		else
+			printf("verdict=%u\n", pos);
+	} else if (strcmp(t->u.user.name, ARPT_ERROR_TARGET) == 0)
+		printf("error=`%s'\n", t->data);
+
+	printf("\n");
+	return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+	unsigned int i;
+	STRUCT_ENTRY_TARGET *ta, *tb;
+	unsigned char *mptr;
+
+	/* Always compare head structures: ignore mask here. */
+	if (a->arp.src.s_addr != b->arp.src.s_addr
+	    || a->arp.tgt.s_addr != b->arp.tgt.s_addr
+	    || a->arp.smsk.s_addr != b->arp.smsk.s_addr
+	    || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr
+	    || a->arp.arhln != b->arp.arhln
+	    || a->arp.arhln_mask != b->arp.arhln_mask
+	    || a->arp.arpop != b->arp.arpop
+	    || a->arp.arpop_mask != b->arp.arpop_mask
+	    || a->arp.arhrd != b->arp.arhrd
+	    || a->arp.arhrd_mask != b->arp.arhrd_mask
+	    || a->arp.arpro != b->arp.arpro
+	    || a->arp.arpro_mask != b->arp.arpro_mask
+	    || a->arp.flags != b->arp.flags
+	    || a->arp.invflags != b->arp.invflags)
+		return 0;
+
+	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++) {
+		if (a->arp.src_devaddr.addr[i] != b->arp.src_devaddr.addr[i] ||
+		    a->arp.src_devaddr.mask[i] != b->arp.src_devaddr.mask[i])
+			return 0;
+		if (a->arp.tgt_devaddr.addr[i] != b->arp.tgt_devaddr.addr[i] ||
+		    a->arp.tgt_devaddr.mask[i] != b->arp.tgt_devaddr.mask[i])
+			return 0;
+	}
+
+	for (i = 0; i < IFNAMSIZ; i++) {
+		if (a->arp.iniface_mask[i] != b->arp.iniface_mask[i])
+			return 0;
+		if ((a->arp.iniface[i] & a->arp.iniface_mask[i])
+		    != (b->arp.iniface[i] & b->arp.iniface_mask[i]))
+			return 0;
+		if (a->arp.outiface_mask[i] != b->arp.outiface_mask[i])
+			return 0;
+		if ((a->arp.outiface[i] & a->arp.outiface_mask[i])
+		    != (b->arp.outiface[i] & b->arp.outiface_mask[i]))
+			return 0;
+	}
+
+	if (/* a->nfcache != b->nfcache
+	    || */a->target_offset != b->target_offset
+	    || a->next_offset != b->next_offset)
+		return 0;
+
+	mptr = matchmask + sizeof(STRUCT_ENTRY);
+/*
+	if (ARPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+		return 0;
+*/
+
+	ta = GET_TARGET((STRUCT_ENTRY *)a);
+	tb = GET_TARGET((STRUCT_ENTRY *)b);
+	if (ta->u.target_size != tb->u.target_size)
+		return 0;
+	if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+		return 0;
+
+	mptr += sizeof(*ta);
+	if (target_different(ta->data, tb->data,
+			     ta->u.target_size - sizeof(*ta), mptr))
+		return 0;
+
+   	return 1;
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct arpt_arp *arp)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*arp)/sizeof(u_int32_t); i++)
+		if (((u_int32_t *)arp)[i])
+			return 0;
+
+	return 1;
+}
+
+/*
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+	assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+	assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+	(*off) += m->u.match_size;
+	return 0;
+}
+*/
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+	    unsigned int user_offset, int *was_return,
+	    TC_HANDLE_T h)
+{
+	unsigned int toff;
+	STRUCT_STANDARD_TARGET *t;
+
+	assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+	assert(e->next_offset >= e->target_offset
+	       + sizeof(STRUCT_ENTRY_TARGET));
+	toff = sizeof(STRUCT_ENTRY);
+/*
+	ARPT_MATCH_ITERATE(e, check_match, &toff);
+*/
+
+	assert(toff == e->target_offset);
+
+	t = (STRUCT_STANDARD_TARGET *)
+		GET_TARGET((STRUCT_ENTRY *)e);
+	/* next_offset will have to be multiple of entry alignment. */
+	assert(e->next_offset == ALIGN(e->next_offset));
+	assert(e->target_offset == ALIGN(e->target_offset));
+	assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+	assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+	if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+		assert(t->target.u.target_size
+		       == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+		assert(t->verdict == -NF_DROP-1
+		       || t->verdict == -NF_ACCEPT-1
+		       || t->verdict == RETURN
+		       || t->verdict < (int)h->entries.size);
+
+		if (t->verdict >= 0) {
+			STRUCT_ENTRY *te = get_entry(h, t->verdict);
+			int idx;
+
+			idx = entry2index(h, te);
+			assert(strcmp(GET_TARGET(te)->u.user.name,
+				      ARPT_ERROR_TARGET)
+			       != 0);
+			assert(te != e);
+
+			/* Prior node must be error node, or this node. */
+			assert(t->verdict == entry2offset(h, e)+e->next_offset
+			       || strcmp(GET_TARGET(index2entry(h, idx-1))
+					 ->u.user.name, ARPT_ERROR_TARGET)
+			       == 0);
+		}
+
+		if (t->verdict == RETURN
+		    && unconditional(&e->arp)
+		    && e->target_offset == sizeof(*e))
+			*was_return = 1;
+		else
+			*was_return = 0;
+	} else if (strcmp(t->target.u.user.name, ARPT_ERROR_TARGET) == 0) {
+		assert(t->target.u.target_size
+		       == ALIGN(sizeof(struct arpt_error_target)));
+
+		/* If this is in user area, previous must have been return */
+		if (*off > user_offset)
+			assert(*was_return);
+
+		*was_return = 0;
+	}
+	else *was_return = 0;
+
+	if (*off == user_offset)
+		assert(strcmp(t->target.u.user.name, ARPT_ERROR_TARGET) == 0);
+
+	(*off) += e->next_offset;
+	(*i)++;
+	return 0;
+}
+
+#ifdef ARPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+	unsigned int i, n;
+	unsigned int user_offset; /* Offset of first user chain */
+	int was_return;
+
+	assert(h->changed == 0 || h->changed == 1);
+	if (strcmp(h->info.name, "filter") == 0) {
+		assert(h->info.valid_hooks
+		       == (1 << NF_IP_LOCAL_IN
+			   | 1 << NF_IP_FORWARD
+			   | 1 << NF_IP_LOCAL_OUT));
+
+		/* Hooks should be first three */
+		assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+		n = get_chain_end(h, 0);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+		n = get_chain_end(h, n);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+	} else if (strcmp(h->info.name, "nat") == 0) {
+		assert((h->info.valid_hooks
+		        == (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_POST_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)) ||
+		       (h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_IN
+			    | 1 << NF_IP_POST_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)));
+
+		assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+		n = get_chain_end(h, n);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+			user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+		}
+
+	} else if (strcmp(h->info.name, "mangle") == 0) {
+		/* This code is getting ugly because linux < 2.4.18-pre6 had
+		 * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+		 * */
+		assert((h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)) || 
+		       (h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_IN
+			    | 1 << NF_IP_FORWARD
+			    | 1 << NF_IP_LOCAL_OUT
+			    | 1 << NF_IP_POST_ROUTING)));
+
+		/* Hooks should be first five */
+		assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+			n = get_chain_end(h, n);
+		}
+
+		if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+			n = get_chain_end(h, n);
+		}
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+			user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+		}
+
+#ifdef NF_IP_DROPPING
+	} else if (strcmp(h->info.name, "drop") == 0) {
+		assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+		/* Hook should be first */
+		assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+		user_offset = 0;
+#endif
+	} else {
+		fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+		abort();
+	}
+
+	/* User chain == end of last builtin + policy entry */
+	user_offset = get_chain_end(h, user_offset);
+	user_offset += get_entry(h, user_offset)->next_offset;
+
+	/* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		STRUCT_ENTRY *e;
+		STRUCT_STANDARD_TARGET *t;
+
+		if (!(h->info.valid_hooks & (1 << i)))
+			continue;
+		assert(h->info.underflow[i]
+		       == get_chain_end(h, h->info.hook_entry[i]));
+
+		e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+		assert(unconditional(&e->ip));
+		assert(e->target_offset == sizeof(*e));
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+		assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+		assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+		assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+		assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+		/* Hooks and underflows must be valid entries */
+		entry2index(h, get_entry(h, h->info.hook_entry[i]));
+		entry2index(h, get_entry(h, h->info.underflow[i]));
+	}
+
+	assert(h->info.size
+	       >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+					 +sizeof(STRUCT_STANDARD_TARGET)));
+
+	assert(h->entries.size
+	       >= (h->new_number
+		   * (sizeof(STRUCT_ENTRY)
+		      + sizeof(STRUCT_STANDARD_TARGET))));
+	assert(strcmp(h->info.name, h->entries.name) == 0);
+
+	i = 0; n = 0;
+	was_return = 0;
+	/* Check all the entries. */
+	ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+		      check_entry, &i, &n, user_offset, &was_return, h);
+
+	assert(i == h->new_number);
+	assert(n == h->entries.size);
+
+	/* Final entry must be error node */
+	assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+		      ->u.user.name,
+		      ERROR_TARGET) == 0);
+}
+#endif /*ARPTC_DEBUG*/
diff --git a/userspace/arptables/libarptc/libarptc_incl.c b/userspace/arptables/libarptc/libarptc_incl.c
new file mode 100644
index 0000000..6322ba3
--- /dev/null
+++ b/userspace/arptables/libarptc/libarptc_incl.c
@@ -0,0 +1,1802 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.7 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+	return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *arptc_fn = NULL;
+
+static const char *hooknames[] =
+{
+	[NF_ARP_IN]		"INPUT",
+	[NF_ARP_OUT]		"OUTPUT",
+	[NF_ARP_FORWARD]	"FORWARD",
+};
+
+struct counter_map
+{
+	enum {
+		COUNTER_MAP_NOMAP,
+		COUNTER_MAP_NORMAL_MAP,
+		COUNTER_MAP_ZEROED,
+		COUNTER_MAP_SET
+	} maptype;
+	unsigned int mappos;
+};
+
+/* Convenience structures */
+struct arpt_error_target
+{
+	STRUCT_ENTRY_TARGET t;
+	char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_cache
+{
+	char name[TABLE_MAXNAMELEN];
+	/* This is the first rule in chain. */
+	STRUCT_ENTRY *start;
+	/* Last rule in chain */
+	STRUCT_ENTRY *end;
+};
+
+STRUCT_TC_HANDLE
+{
+	/* Have changes been made? */
+	int changed;
+	/* Size in here reflects original state. */
+	STRUCT_GETINFO info;
+
+	struct counter_map *counter_map;
+	/* Array of hook names */
+	const char **hooknames;
+
+	/* Cached position of chain heads (NULL = no cache). */
+	unsigned int cache_num_chains;
+	unsigned int cache_num_builtins;
+	struct chain_cache *cache_chain_heads;
+
+	/* Chain iterator: current chain cache entry. */
+	struct chain_cache *cache_chain_iteration;
+
+	/* Rule iterator: terminal rule */
+	STRUCT_ENTRY *cache_rule_end;
+
+	/* Number in here reflects current state. */
+	unsigned int new_number;
+	STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+	if (h->cache_chain_heads) {
+		free(h->cache_chain_heads);
+		h->cache_chain_heads = NULL;
+		h->cache_num_chains = 0;
+		h->cache_chain_iteration = NULL;
+		h->cache_rule_end = NULL;
+	}
+	h->changed = 1;
+}
+
+#ifdef ARPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("ARPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+	   const STRUCT_ENTRY *seek,
+	   unsigned int *pos)
+{
+	if (i == seek)
+		return 1;
+	(*pos)++;
+	return 0;
+}
+
+static unsigned int
+entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+	unsigned int pos = 0;
+
+	if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+			  get_number, seek, &pos) == 0) {
+		fprintf(stderr, "ERROR: offset %zu not an entry!\n",
+			(char *)seek - (char *)h->entries.entrytable);
+		abort();
+	}
+	return pos;
+}
+
+static inline int
+get_entry_n(STRUCT_ENTRY *i,
+	    unsigned int number,
+	    unsigned int *pos,
+	    STRUCT_ENTRY **pe)
+{
+	if (*pos == number) {
+		*pe = i;
+		return 1;
+	}
+	(*pos)++;
+	return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+	unsigned int pos = 0;
+	STRUCT_ENTRY *ret = NULL;
+
+	ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+		      get_entry_n, index, &pos, &ret);
+
+	return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+	return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+	return (char *)e - (char *)h->entries.entrytable;
+}
+
+static unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+	return entry2offset(h, index2entry(h, index));
+}
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+	STRUCT_ENTRY *e;
+
+	e = get_entry(h, offset);
+	if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
+		fprintf(stderr, "ERROR: offset %u not an error node!\n",
+			offset);
+		abort();
+	}
+
+	return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+	size_t len;
+	TC_HANDLE_T h;
+
+	len = sizeof(STRUCT_TC_HANDLE)
+		+ size
+		+ num_rules * sizeof(struct counter_map);
+
+	if ((h = malloc(len)) == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	h->changed = 0;
+	h->cache_num_chains = 0;
+	h->cache_chain_heads = NULL;
+	h->counter_map = (void *)h
+		+ sizeof(STRUCT_TC_HANDLE)
+		+ size;
+	strcpy(h->info.name, tablename);
+	strcpy(h->entries.name, tablename);
+
+	return h;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+	TC_HANDLE_T h;
+	STRUCT_GETINFO info;
+	unsigned int i;
+	socklen_t s, tmp;
+
+	arptc_fn = TC_INIT;
+
+	if (sockfd != -1)
+		close(sockfd);
+
+	sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+	if (sockfd < 0)
+		return NULL;
+
+	s = sizeof(info);
+	if (RUNTIME_NF_ARP_NUMHOOKS == 2)
+		s -= 2 * sizeof(unsigned int);
+
+	if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+		errno = EINVAL;
+		return NULL;
+	}
+	strcpy(info.name, tablename);
+	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+		return NULL;
+
+	if (RUNTIME_NF_ARP_NUMHOOKS == 2) {
+		memmove(&(info.hook_entry[3]), &(info.hook_entry[2]),
+		5 * sizeof(unsigned int));
+		memmove(&(info.underflow[3]), &(info.underflow[2]),
+		2 * sizeof(unsigned int));
+	}
+
+	if ((h = alloc_handle(info.name, info.size, info.num_entries))
+	    == NULL)
+		return NULL;
+
+/* Too hard --RR */
+#if 0
+	sprintf(pathname, "%s/%s", ARPT_LIB_DIR, info.name);
+	dynlib = dlopen(pathname, RTLD_NOW);
+	if (!dynlib) {
+		errno = ENOENT;
+		return NULL;
+	}
+	h->hooknames = dlsym(dynlib, "hooknames");
+	if (!h->hooknames) {
+		errno = ENOENT;
+		return NULL;
+	}
+#else
+	h->hooknames = hooknames;
+#endif
+
+	/* Initialize current state */
+	h->info = info;
+	h->new_number = h->info.num_entries;
+	for (i = 0; i < h->info.num_entries; i++)
+		h->counter_map[i]
+			= ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
+
+	h->entries.size = h->info.size;
+
+	tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
+		       &tmp) < 0) {
+		free(h);
+		return NULL;
+	}
+
+	CHECK(h);
+	return h;
+}
+
+/*
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+	printf("Match name: `%s'\n", m->u.user.name);
+	return 0;
+}
+*/
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+ 
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+	CHECK(handle);
+
+	printf("libarptc v%s.  %u entries, %u bytes.\n",
+	       ARPTABLES_VERSION,
+	       handle->new_number, handle->entries.size);
+	printf("Table `%s'\n", handle->info.name);
+	printf("Hooks: in/out = %u/%u\n",
+	       handle->info.hook_entry[NF_ARP_IN],
+	       handle->info.hook_entry[NF_ARP_OUT]);
+	printf("Underflows: in/out = %u/%u\n",
+	       handle->info.underflow[NF_ARP_IN],
+	       handle->info.underflow[NF_ARP_OUT]);
+
+	ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+		      dump_entry, handle);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+	unsigned int i;
+
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		if ((h->info.valid_hooks & (1 << i))
+		    && get_entry(h, h->info.hook_entry[i]) == e)
+			return i+1;
+	}
+	return 0;
+}
+
+static inline int
+add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+{
+	unsigned int builtin;
+
+	/* Last entry.  End it. */
+	if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+		/* This is the ERROR node at end of the table */
+		h->cache_chain_heads[h->cache_num_chains-1].end = *prev;
+		return 0;
+	}
+
+	/* We know this is the start of a new chain if it's an ERROR
+	   target, or a hook entry point */
+	if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+		/* prev was last entry in previous chain */
+		h->cache_chain_heads[h->cache_num_chains-1].end
+			= *prev;
+
+		strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+		       (const char *)GET_TARGET(e)->data);
+		h->cache_chain_heads[h->cache_num_chains].start
+			= (void *)e + e->next_offset;
+		h->cache_num_chains++;
+	} else if ((builtin = is_hook_entry(e, h)) != 0) {
+		if (h->cache_num_chains > 0)
+			/* prev was last entry in previous chain */
+			h->cache_chain_heads[h->cache_num_chains-1].end
+				= *prev;
+
+		strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+		       h->hooknames[builtin-1]);
+		h->cache_chain_heads[h->cache_num_chains].start
+			= (void *)e;
+		h->cache_num_chains++;
+	}
+
+	*prev = e;
+	return 0;
+}
+
+static int alphasort(const void *a, const void *b)
+{
+	return strcmp(((struct chain_cache *)a)->name,
+		      ((struct chain_cache *)b)->name);
+}
+
+static int populate_cache(TC_HANDLE_T h)
+{
+	unsigned int i;
+	STRUCT_ENTRY *prev;
+
+	/* # chains < # rules / 2 + num builtins - 1 */
+	h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+				      * sizeof(struct chain_cache));
+	if (!h->cache_chain_heads) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	h->cache_num_chains = 0;
+	h->cache_num_builtins = 0;
+
+	/* Count builtins */
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		if (h->info.valid_hooks & (1 << i))
+			h->cache_num_builtins++;
+	}
+
+	prev = NULL;
+	ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+		      add_chain, h, &prev);
+
+	qsort(h->cache_chain_heads + h->cache_num_builtins,
+	      h->cache_num_chains - h->cache_num_builtins,
+	      sizeof(struct chain_cache), alphasort);
+
+	return 1;
+}
+
+/* Returns cache ptr if found, otherwise NULL. */
+static struct chain_cache *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+	unsigned int i;
+
+	if (handle->cache_chain_heads == NULL
+	    && !populate_cache(handle))
+		return NULL;
+
+	/* FIXME: Linear search through builtins, then binary --RR */
+	for (i = 0; i < handle->cache_num_chains; i++) {
+		if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+			return &handle->cache_chain_heads[i];
+	}
+
+	return NULL;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+	return find_label(chain, handle) != NULL;
+}
+
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+	unsigned int last_off, off;
+	STRUCT_ENTRY *e;
+
+	last_off = start;
+	e = get_entry(handle, start);
+
+	/* Terminate when we meet a error label or a hook entry. */
+	for (off = start + e->next_offset;
+	     off < handle->entries.size;
+	     last_off = off, off += e->next_offset) {
+		STRUCT_ENTRY_TARGET *t;
+		unsigned int i;
+
+		e = get_entry(handle, off);
+
+		/* We hit an entry point. */
+		for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+			if ((handle->info.valid_hooks & (1 << i))
+			    && off == handle->info.hook_entry[i])
+				return last_off;
+		}
+
+		/* We hit a user chain label */
+		t = GET_TARGET(e);
+		if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
+			return last_off;
+	}
+	/* SHOULD NEVER HAPPEN */
+	fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+		handle->entries.size, off);
+	abort();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+	if ((*handle)->cache_chain_heads == NULL
+	    && !populate_cache(*handle))
+		return NULL;
+
+	(*handle)->cache_chain_iteration
+		= &(*handle)->cache_chain_heads[0];
+
+	return (*handle)->cache_chain_iteration->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+	(*handle)->cache_chain_iteration++;
+
+	if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+	    == (*handle)->cache_num_chains)
+		return NULL;
+
+	return (*handle)->cache_chain_iteration->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+	struct chain_cache *c;
+
+	c = find_label(chain, *handle);
+	if (!c) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	/* Empty chain: single return/policy rule */
+	if (c->start == c->end)
+		return NULL;
+
+	(*handle)->cache_rule_end = c->end;
+	return c->start;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+	if ((void *)prev + prev->next_offset
+	    == (void *)(*handle)->cache_rule_end)
+		return NULL;
+
+	return (void *)prev + prev->next_offset;
+}
+
+#if 0
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+	unsigned int off = 0;
+	STRUCT_ENTRY *start, *end;
+
+	CHECK(*handle);
+	if (!find_label(&off, chain, *handle)) {
+		errno = ENOENT;
+		return (unsigned int)-1;
+	}
+
+	start = get_entry(*handle, off);
+	end = get_entry(*handle, get_chain_end(*handle, off));
+
+	return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+				unsigned int n,
+				TC_HANDLE_T *handle)
+{
+	unsigned int pos = 0, chainindex;
+
+	CHECK(*handle);
+	if (!find_label(&pos, chain, *handle)) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+	return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+	int spos;
+	const unsigned char *data;
+	unsigned int labelidx;
+	STRUCT_ENTRY *jumpto;
+
+	/* To avoid const warnings */
+	STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+
+	if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
+		return GET_TARGET(e)->u.user.name;
+
+	/* Standard target: evaluate */
+	data = GET_TARGET(e)->data;
+	spos = *(const int *)data;
+	if (spos < 0) {
+		if (spos == RETURN)
+			return LABEL_RETURN;
+		else if (spos == -NF_ACCEPT-1)
+			return LABEL_ACCEPT;
+		else if (spos == -NF_DROP-1)
+			return LABEL_DROP;
+		else if (spos == -NF_QUEUE-1)
+			return LABEL_QUEUE;
+
+		fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
+			entry2offset(handle, e), handle->entries.size,
+			spos);
+		abort();
+	}
+
+	jumpto = get_entry(handle, spos);
+
+	/* Fall through rule */
+	if (jumpto == (void *)e + e->next_offset)
+		return "";
+
+	/* Must point to head of a chain: ie. after error rule */
+	labelidx = entry2index(handle, jumpto) - 1;
+	return get_errorlabel(handle, index2offset(handle, labelidx));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+			  TC_HANDLE_T *handle)
+{
+	return target_name(*handle, e);
+}
+
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+	unsigned int i;
+
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		if ((handle->info.valid_hooks & (1 << i))
+		    && handle->hooknames[i]
+		    && strcmp(handle->hooknames[i], chain) == 0)
+			return i+1;
+	}
+	return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+	      STRUCT_COUNTERS *counters,
+	      TC_HANDLE_T *handle)
+{
+	unsigned int start;
+	STRUCT_ENTRY *e;
+	int hook;
+
+	hook = TC_BUILTIN(chain, *handle);
+	if (hook != 0)
+		start = (*handle)->info.hook_entry[hook-1];
+	else
+		return NULL;
+
+	e = get_entry(*handle, get_chain_end(*handle, start));
+	*counters = e->counters;
+
+	return target_name(*handle, e);
+}
+
+static int
+correct_verdict(STRUCT_ENTRY *e,
+		char *base,
+		unsigned int offset, int delta_offset)
+{
+	STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
+	unsigned int curr = (char *)e - base;
+
+	/* Trap: insert of fall-through rule.  Don't change fall-through
+	   verdict to jump-over-next-rule. */
+	if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
+	    && t->verdict > (int)offset
+	    && !(curr == offset &&
+		 t->verdict == curr + e->next_offset)) {
+		t->verdict += delta_offset;
+	}
+
+	return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+{
+	ENTRY_ITERATE((*handle)->entries.entrytable,
+		      (*handle)->entries.size,
+		      correct_verdict, (char *)(*handle)->entries.entrytable,
+		      offset, delta_offset);
+
+	set_changed(*handle);
+	return 1;
+}
+
+/* If prepend is set, then we are prepending to a chain: if the
+ * insertion position is an entry point, keep the entry point. */
+static int
+insert_rules(unsigned int num_rules, unsigned int rules_size,
+	     const STRUCT_ENTRY *insert,
+	     unsigned int offset, unsigned int num_rules_offset,
+	     int prepend,
+	     TC_HANDLE_T *handle)
+{
+	TC_HANDLE_T newh;
+	STRUCT_GETINFO newinfo;
+	unsigned int i;
+
+	if (offset >= (*handle)->entries.size) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	newinfo = (*handle)->info;
+
+	/* Fix up entry points. */
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		/* Entry points to START of chain, so keep same if
+                   inserting on at that point. */
+		if ((*handle)->info.hook_entry[i] > offset)
+			newinfo.hook_entry[i] += rules_size;
+
+		/* Underflow always points to END of chain (policy),
+		   so if something is inserted at same point, it
+		   should be advanced. */
+		if ((*handle)->info.underflow[i] >= offset)
+			newinfo.underflow[i] += rules_size;
+	}
+
+	newh = alloc_handle((*handle)->info.name,
+			    (*handle)->entries.size + rules_size,
+			    (*handle)->new_number + num_rules);
+	if (!newh)
+		return 0;
+	newh->info = newinfo;
+
+	/* Copy pre... */
+	memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
+	/* ... Insert new ... */
+	memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
+	/* ... copy post */
+	memcpy((char *)newh->entries.entrytable + offset + rules_size,
+	       (char *)(*handle)->entries.entrytable + offset,
+	       (*handle)->entries.size - offset);
+
+	/* Move counter map. */
+	/* Copy pre... */
+	memcpy(newh->counter_map, (*handle)->counter_map,
+	       sizeof(struct counter_map) * num_rules_offset);
+	/* ... copy post */
+	memcpy(newh->counter_map + num_rules_offset + num_rules,
+	       (*handle)->counter_map + num_rules_offset,
+	       sizeof(struct counter_map) * ((*handle)->new_number
+					     - num_rules_offset));
+	/* Set intermediates to no counter copy */
+	for (i = 0; i < num_rules; i++)
+		newh->counter_map[num_rules_offset+i]
+			= ((struct counter_map){ COUNTER_MAP_SET, 0 });
+
+	newh->new_number = (*handle)->new_number + num_rules;
+	newh->entries.size = (*handle)->entries.size + rules_size;
+	newh->hooknames = (*handle)->hooknames;
+
+	if ((*handle)->cache_chain_heads)
+		free((*handle)->cache_chain_heads);
+	free(*handle);
+	*handle = newh;
+
+	return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+	     unsigned int offset, unsigned int num_rules_offset,
+	     TC_HANDLE_T *handle)
+{
+	unsigned int i;
+
+	if (offset + rules_size > (*handle)->entries.size) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* Fix up entry points. */
+	for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) {
+		/* In practice, we never delete up to a hook entry,
+		   since the built-in chains are always first,
+		   so these two are never equal */
+		if ((*handle)->info.hook_entry[i] >= offset + rules_size)
+			(*handle)->info.hook_entry[i] -= rules_size;
+		else if ((*handle)->info.hook_entry[i] > offset) {
+			fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
+				i, (*handle)->info.hook_entry[i], offset);
+			abort();
+		}
+
+		/* Underflow points to policy (terminal) rule in
+                   built-in, so sequality is valid here (when deleting
+                   the last rule). */
+		if ((*handle)->info.underflow[i] >= offset + rules_size)
+			(*handle)->info.underflow[i] -= rules_size;
+		else if ((*handle)->info.underflow[i] > offset) {
+			fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
+				i, (*handle)->info.underflow[i], offset);
+			abort();
+		}
+	}
+
+	/* Move the rules down. */
+	memmove((char *)(*handle)->entries.entrytable + offset,
+		(char *)(*handle)->entries.entrytable + offset + rules_size,
+		(*handle)->entries.size - (offset + rules_size));
+
+	/* Move the counter map down. */
+	memmove(&(*handle)->counter_map[num_rules_offset],
+		&(*handle)->counter_map[num_rules_offset + num_rules],
+		sizeof(struct counter_map)
+		* ((*handle)->new_number - (num_rules + num_rules_offset)));
+
+	/* Fix numbers */
+	(*handle)->new_number -= num_rules;
+	(*handle)->entries.size -= rules_size;
+
+	return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+	STRUCT_STANDARD_TARGET *t;
+
+	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+	if (t->target.u.target_size
+	    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+		errno = EINVAL;
+		return 0;
+	}
+	/* memset for memcmp convenience on delete/replace */
+	memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+	strcpy(t->target.u.user.name, STANDARD_TARGET);
+	t->verdict = verdict;
+
+	return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+	   STRUCT_ENTRY *e,
+	   unsigned int offset,
+	   STRUCT_ENTRY_TARGET *old)
+{
+	STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+	/* Save old target (except data, which we don't change, except for
+	   standard case, where we don't care). */
+	*old = *t;
+
+	/* Maybe it's empty (=> fall through) */
+	if (strcmp(t->u.user.name, "") == 0)
+		return standard_map(e, offset + e->next_offset);
+	/* Maybe it's a standard target name... */
+	else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+		return standard_map(e, -NF_ACCEPT - 1);
+	else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+		return standard_map(e, -NF_DROP - 1);
+	else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+		return standard_map(e, -NF_QUEUE - 1);
+	else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+		return standard_map(e, RETURN);
+	else if (TC_BUILTIN(t->u.user.name, handle)) {
+		/* Can't jump to builtins. */
+		errno = EINVAL;
+		return 0;
+	} else {
+		/* Maybe it's an existing chain name. */
+		struct chain_cache *c;
+
+		c = find_label(t->u.user.name, handle);
+		if (c)
+			return standard_map(e, entry2offset(handle, c->start));
+	}
+
+	/* Must be a module?  If not, kernel will reject... */
+	/* memset to all 0 for your memcmp convenience. */
+	memset(t->u.user.name + strlen(t->u.user.name),
+	       0,
+	       FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+	return 1;
+}
+
+static void
+unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
+{
+	STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+	/* Save old target (except data, which we don't change, except for
+	   standard case, where we don't care). */
+	*t = *old;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const ARPT_CHAINLABEL chain,
+		const STRUCT_ENTRY *e,
+		unsigned int rulenum,
+		TC_HANDLE_T *handle)
+{
+	unsigned int chainindex, offset;
+	STRUCT_ENTRY_TARGET old;
+	struct chain_cache *c;
+	STRUCT_ENTRY *tmp;
+	int ret;
+
+	arptc_fn = TC_INSERT_ENTRY;
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	chainindex = entry2index(*handle, c->start);
+
+	tmp = index2entry(*handle, chainindex + rulenum);
+	if (!tmp || tmp > c->end) {
+		errno = E2BIG;
+		return 0;
+	}
+	offset = index2offset(*handle, chainindex + rulenum);
+
+	/* Mapping target actually alters entry, but that's
+           transparent to the caller. */
+	if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+		return 0;
+
+	ret = insert_rules(1, e->next_offset, e, offset,
+			   chainindex + rulenum, rulenum == 0, handle);
+	unmap_target((STRUCT_ENTRY *)e, &old);
+	return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const ARPT_CHAINLABEL chain,
+		 const STRUCT_ENTRY *e,
+		 unsigned int rulenum,
+		 TC_HANDLE_T *handle)
+{
+	unsigned int chainindex, offset;
+	STRUCT_ENTRY_TARGET old;
+	struct chain_cache *c;
+	STRUCT_ENTRY *tmp;
+	int ret;
+
+	arptc_fn = TC_REPLACE_ENTRY;
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	chainindex = entry2index(*handle, c->start);
+
+	tmp = index2entry(*handle, chainindex + rulenum);
+	if (!tmp || tmp >= c->end) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	offset = index2offset(*handle, chainindex + rulenum);
+	/* Replace = delete and insert. */
+	if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
+			  offset, chainindex + rulenum, handle))
+		return 0;
+
+	if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+		return 0;
+
+	ret = insert_rules(1, e->next_offset, e, offset,
+			   chainindex + rulenum, 1, handle);
+	unmap_target((STRUCT_ENTRY *)e, &old);
+	return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const ARPT_CHAINLABEL chain,
+		const STRUCT_ENTRY *e,
+		TC_HANDLE_T *handle)
+{
+	struct chain_cache *c;
+	STRUCT_ENTRY_TARGET old;
+	int ret;
+
+	arptc_fn = TC_APPEND_ENTRY;
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (!map_target(*handle, (STRUCT_ENTRY *)e,
+			entry2offset(*handle, c->end), &old))
+		return 0;
+
+	ret = insert_rules(1, e->next_offset, e,
+			   entry2offset(*handle, c->end),
+			   entry2index(*handle, c->end),
+			   0, handle);
+	unmap_target((STRUCT_ENTRY *)e, &old);
+	return ret;
+}
+
+/*
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+		const unsigned char *a_elems,
+		const unsigned char *b_elems,
+		unsigned char **maskptr)
+{
+	const STRUCT_ENTRY_MATCH *b;
+	unsigned int i;
+*/
+	/* Offset of b is the same as a. */
+/*
+	b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+	if (a->u.match_size != b->u.match_size)
+		return 1;
+
+	if (strcmp(a->u.user.name, b->u.user.name) != 0)
+		return 1;
+
+	*maskptr += ALIGN(sizeof(*a));
+
+	for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+		if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+			return 1;
+	*maskptr += i;
+	return 0;
+}
+*/
+
+static inline int
+target_different(const unsigned char *a_targdata,
+		 const unsigned char *b_targdata,
+		 unsigned int tdatasize,
+		 const unsigned char *mask)
+{
+	unsigned int i;
+	for (i = 0; i < tdatasize; i++)
+		if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
+			return 1;
+
+	return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+	const STRUCT_ENTRY *b,
+	unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const ARPT_CHAINLABEL chain,
+		const STRUCT_ENTRY *origfw,
+		unsigned char *matchmask,
+		TC_HANDLE_T *handle)
+{
+	unsigned int offset;
+	struct chain_cache *c;
+	STRUCT_ENTRY *e, *fw;
+
+	arptc_fn = TC_DELETE_ENTRY;
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	fw = malloc(origfw->next_offset);
+	if (fw == NULL) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	for (offset = entry2offset(*handle, c->start);
+	     offset < entry2offset(*handle, c->end);
+	     offset += e->next_offset) {
+		STRUCT_ENTRY_TARGET discard;
+
+		memcpy(fw, origfw, origfw->next_offset);
+
+		/* FIXME: handle this in is_same --RR */
+		if (!map_target(*handle, fw, offset, &discard)) {
+			free(fw);
+			return 0;
+		}
+		e = get_entry(*handle, offset);
+
+#if 0
+		printf("Deleting:\n");
+		dump_entry(newe);
+#endif
+		if (is_same(e, fw, matchmask)) {
+			int ret;
+			ret = delete_rules(1, e->next_offset,
+					   offset, entry2index(*handle, e),
+					   handle);
+			free(fw);
+			return ret;
+		}
+	}
+
+	free(fw);
+	errno = ENOENT;
+	return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const ARPT_CHAINLABEL chain,
+		    unsigned int rulenum,
+		    TC_HANDLE_T *handle)
+{
+	unsigned int index;
+	int ret;
+	STRUCT_ENTRY *e;
+	struct chain_cache *c;
+
+	arptc_fn = TC_DELETE_NUM_ENTRY;
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	index = entry2index(*handle, c->start) + rulenum;
+
+	if (index >= entry2index(*handle, c->end)) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	e = index2entry(*handle, index);
+	if (e == NULL) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+			   index, handle);
+	return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const ARPT_CHAINLABEL chain,
+		STRUCT_ENTRY *entry,
+		TC_HANDLE_T *handle)
+{
+	errno = ENOSYS;
+	return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+	unsigned int startindex, endindex;
+	struct chain_cache *c;
+	int ret;
+
+	arptc_fn = TC_FLUSH_ENTRIES;
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+	startindex = entry2index(*handle, c->start);
+	endindex = entry2index(*handle, c->end);
+
+	ret = delete_rules(endindex - startindex,
+			   (char *)c->end - (char *)c->start,
+			   entry2offset(*handle, c->start), startindex,
+			   handle);
+	return ret;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+	unsigned int i, end;
+	struct chain_cache *c;
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	i = entry2index(*handle, c->start);
+	end = entry2index(*handle, c->end);
+
+	for (; i <= end; i++) {
+		if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
+			(*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+	}
+	set_changed(*handle);
+
+	return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const ARPT_CHAINLABEL chain,
+		unsigned int rulenum,
+		TC_HANDLE_T *handle)
+{
+	STRUCT_ENTRY *e;
+	struct chain_cache *c;
+	unsigned int chainindex, end;
+
+	arptc_fn = TC_READ_COUNTER;
+	CHECK(*handle);
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	chainindex = entry2index(*handle, c->start);
+	end = entry2index(*handle, c->end);
+
+	if (chainindex + rulenum > end) {
+		errno = E2BIG;
+		return NULL;
+	}
+
+	e = index2entry(*handle, chainindex + rulenum);
+
+	return &e->counters;
+}
+
+int
+TC_ZERO_COUNTER(const ARPT_CHAINLABEL chain,
+		unsigned int rulenum,
+		TC_HANDLE_T *handle)
+{
+	STRUCT_ENTRY *e;
+	struct chain_cache *c;
+	unsigned int chainindex, end;
+	
+	arptc_fn = TC_ZERO_COUNTER;
+	CHECK(*handle);
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	chainindex = entry2index(*handle, c->start);
+	end = entry2index(*handle, c->end);
+
+	if (chainindex + rulenum > end) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	e = index2entry(*handle, chainindex + rulenum);
+
+	if ((*handle)->counter_map[chainindex + rulenum].maptype
+			== COUNTER_MAP_NORMAL_MAP) {
+		(*handle)->counter_map[chainindex + rulenum].maptype
+			 = COUNTER_MAP_ZEROED;
+	}
+
+	set_changed(*handle);
+
+	return 1;
+}
+
+int 
+TC_SET_COUNTER(const ARPT_CHAINLABEL chain,
+	       unsigned int rulenum,
+	       STRUCT_COUNTERS *counters,
+	       TC_HANDLE_T *handle)
+{
+	STRUCT_ENTRY *e;
+	struct chain_cache *c;
+	unsigned int chainindex, end;
+
+	arptc_fn = TC_SET_COUNTER;
+	CHECK(*handle);
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	chainindex = entry2index(*handle, c->start);
+	end = entry2index(*handle, c->end);
+
+	if (chainindex + rulenum > end) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	e = index2entry(*handle, chainindex + rulenum);
+
+	(*handle)->counter_map[chainindex + rulenum].maptype
+		= COUNTER_MAP_SET;
+
+	memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+	set_changed(*handle);
+
+	return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+	int ret;
+	struct {
+		STRUCT_ENTRY head;
+		struct arpt_error_target name;
+		STRUCT_ENTRY ret;
+		STRUCT_STANDARD_TARGET target;
+	} newc;
+
+	arptc_fn = TC_CREATE_CHAIN;
+
+	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+	if (find_label(chain, *handle)
+	    || strcmp(chain, LABEL_DROP) == 0
+	    || strcmp(chain, LABEL_ACCEPT) == 0
+	    || strcmp(chain, LABEL_QUEUE) == 0
+	    || strcmp(chain, LABEL_RETURN) == 0) {
+		errno = EEXIST;
+		return 0;
+	}
+
+	if (strlen(chain)+1 > sizeof(ARPT_CHAINLABEL)) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	memset(&newc, 0, sizeof(newc));
+	newc.head.target_offset = sizeof(STRUCT_ENTRY);
+	newc.head.next_offset
+		= sizeof(STRUCT_ENTRY)
+		+ ALIGN(sizeof(struct arpt_error_target));
+	strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+	newc.name.t.u.target_size = ALIGN(sizeof(struct arpt_error_target));
+	strcpy(newc.name.error, chain);
+
+	newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+	newc.ret.next_offset
+		= sizeof(STRUCT_ENTRY)
+		+ ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+	strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+	newc.target.target.u.target_size
+		= ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+	newc.target.verdict = RETURN;
+
+	/* Add just before terminal entry */
+	ret = insert_rules(2, sizeof(newc), &newc.head,
+			   index2offset(*handle, (*handle)->new_number - 1),
+			   (*handle)->new_number - 1,
+			   0, handle);
+	return ret;
+}
+
+static int
+count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
+{
+	STRUCT_STANDARD_TARGET *t;
+
+	if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+		if (t->verdict == offset)
+			(*ref)++;
+	}
+
+	return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const ARPT_CHAINLABEL chain,
+		  TC_HANDLE_T *handle)
+{
+	struct chain_cache *c;
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	*ref = 0;
+	ENTRY_ITERATE((*handle)->entries.entrytable,
+		      (*handle)->entries.size,
+		      count_ref, entry2offset(*handle, c->start), ref);
+	return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+	unsigned int labelidx, labeloff;
+	unsigned int references;
+	struct chain_cache *c;
+	int ret;
+
+	if (!TC_GET_REFERENCES(&references, chain, handle))
+		return 0;
+
+	arptc_fn = TC_DELETE_CHAIN;
+
+	if (TC_BUILTIN(chain, *handle)) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	if (references > 0) {
+		errno = EMLINK;
+		return 0;
+	}
+
+	if (!(c = find_label(chain, *handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if ((void *)c->start != c->end) {
+		errno = ENOTEMPTY;
+		return 0;
+	}
+
+	/* Need label index: preceeds chain start */
+	labelidx = entry2index(*handle, c->start) - 1;
+	labeloff = index2offset(*handle, labelidx);
+
+	ret = delete_rules(2,
+			   get_entry(*handle, labeloff)->next_offset
+			   + c->start->next_offset,
+			   labeloff, labelidx, handle);
+	return ret;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const ARPT_CHAINLABEL oldname,
+		    const ARPT_CHAINLABEL newname,
+		    TC_HANDLE_T *handle)
+{
+	unsigned int labeloff, labelidx;
+	struct chain_cache *c;
+	struct arpt_error_target *t;
+
+	arptc_fn = TC_RENAME_CHAIN;
+
+	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+	if (find_label(newname, *handle)
+	    || strcmp(newname, LABEL_DROP) == 0
+	    || strcmp(newname, LABEL_ACCEPT) == 0
+	    || strcmp(newname, LABEL_QUEUE) == 0
+	    || strcmp(newname, LABEL_RETURN) == 0) {
+		errno = EEXIST;
+		return 0;
+	}
+
+	if (!(c = find_label(oldname, *handle))
+	    || TC_BUILTIN(oldname, *handle)) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (strlen(newname)+1 > sizeof(ARPT_CHAINLABEL)) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* Need label index: preceeds chain start */
+	labelidx = entry2index(*handle, c->start) - 1;
+	labeloff = index2offset(*handle, labelidx);
+
+	t = (struct arpt_error_target *)
+		GET_TARGET(get_entry(*handle, labeloff));
+
+	memset(t->error, 0, sizeof(t->error));
+	strcpy(t->error, newname);
+	set_changed(*handle);
+
+	return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const ARPT_CHAINLABEL chain,
+	      const ARPT_CHAINLABEL policy,
+	      STRUCT_COUNTERS *counters,
+	      TC_HANDLE_T *handle)
+{
+	unsigned int hook;
+	unsigned int policyoff, ctrindex;
+	STRUCT_ENTRY *e;
+	STRUCT_STANDARD_TARGET *t;
+
+	arptc_fn = TC_SET_POLICY;
+	/* Figure out which chain. */
+	hook = TC_BUILTIN(chain, *handle);
+	if (hook == 0) {
+		errno = ENOENT;
+		return 0;
+	} else
+		hook--;
+
+	policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
+	if (policyoff != (*handle)->info.underflow[hook]) {
+		printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
+		       chain, policyoff, (*handle)->info.underflow[hook]);
+		return 0;
+	}
+
+	e = get_entry(*handle, policyoff);
+	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+	if (strcmp(policy, LABEL_ACCEPT) == 0)
+		t->verdict = -NF_ACCEPT - 1;
+	else if (strcmp(policy, LABEL_DROP) == 0)
+		t->verdict = -NF_DROP - 1;
+	else {
+		errno = EINVAL;
+		return 0;
+	}
+
+	ctrindex = entry2index(*handle, e);
+
+	if (counters) {
+		/* set byte and packet counters */
+		memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+		(*handle)->counter_map[ctrindex].maptype
+			= COUNTER_MAP_SET;
+
+	} else {
+		(*handle)->counter_map[ctrindex]
+			= ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+	}
+
+	set_changed(*handle);
+
+	return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libarptc.c: In function `TC_COMMIT':
+   libarptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+		  const STRUCT_COUNTERS *a,
+		  const STRUCT_COUNTERS *b)
+{
+	answer->pcnt = a->pcnt - b->pcnt;
+	answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+	/* Replace, then map back the counters. */
+	STRUCT_REPLACE *repl;
+	STRUCT_COUNTERS_INFO *newcounters;
+	unsigned int i;
+	size_t counterlen
+		= sizeof(STRUCT_COUNTERS_INFO)
+		+ sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
+	int sizeof_repl = sizeof(*repl);
+
+	CHECK(*handle);
+#if 0
+	TC_DUMP_ENTRIES(*handle);
+#endif
+
+	/* Don't commit if nothing changed. */
+	if (!(*handle)->changed)
+		goto finished;
+
+	/* allocate a bit more than needed for ease */
+	repl = malloc(2 * sizeof(*repl) + (*handle)->entries.size);
+	if (!repl) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	/* These are the old counters we will get from kernel */
+	repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+				* (*handle)->info.num_entries);
+	if (!repl->counters) {
+		free(repl);
+		errno = ENOMEM;
+		return 0;
+	}
+
+	/* These are the counters we're going to put back, later. */
+	newcounters = malloc(counterlen);
+	if (!newcounters) {
+		free(repl->counters);
+		free(repl);
+		errno = ENOMEM;
+		return 0;
+	}
+
+	strcpy(repl->name, (*handle)->info.name);
+	repl->num_entries = (*handle)->new_number;
+	repl->size = (*handle)->entries.size;
+	memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+	       sizeof(repl->hook_entry));
+	memcpy(repl->underflow, (*handle)->info.underflow,
+	       sizeof(repl->underflow));
+	repl->num_counters = (*handle)->info.num_entries;
+	repl->valid_hooks = (*handle)->info.valid_hooks;
+	memcpy(repl->entries, (*handle)->entries.entrytable,
+	       (*handle)->entries.size);
+
+	if (RUNTIME_NF_ARP_NUMHOOKS == 2) {
+		memmove(&(repl->underflow[2]), &(repl->underflow[3]),
+		((*handle)->entries.size) + sizeof(struct arpt_replace));
+		memmove(&(repl->hook_entry[2]), &(repl->hook_entry[3]),
+		((*handle)->entries.size) + sizeof(struct arpt_replace));
+		sizeof_repl -= 2 * sizeof(unsigned int);
+	}
+
+	if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+		       sizeof_repl + (*handle)->entries.size) < 0) {
+		free(repl->counters);
+		free(repl);
+		free(newcounters);
+		return 0;
+	}
+
+	if (RUNTIME_NF_ARP_NUMHOOKS == 2) {
+		memmove(&(repl->hook_entry[3]), &(repl->hook_entry[2]),
+		((*handle)->entries.size) + sizeof(struct arpt_replace));
+		memmove(&(repl->underflow[3]), &(repl->underflow[2]),
+		((*handle)->entries.size) + sizeof(struct arpt_replace));
+	}
+
+	/* Put counters back. */
+	strcpy(newcounters->name, (*handle)->info.name);
+	newcounters->num_counters = (*handle)->new_number;
+	for (i = 0; i < (*handle)->new_number; i++) {
+		unsigned int mappos = (*handle)->counter_map[i].mappos;
+		switch ((*handle)->counter_map[i].maptype) {
+		case COUNTER_MAP_NOMAP:
+			newcounters->counters[i]
+				= ((STRUCT_COUNTERS){ 0, 0 });
+			break;
+
+		case COUNTER_MAP_NORMAL_MAP:
+			/* Original read: X.
+			 * Atomic read on replacement: X + Y.
+			 * Currently in kernel: Z.
+			 * Want in kernel: X + Y + Z.
+			 * => Add in X + Y
+			 * => Add in replacement read.
+			 */
+			newcounters->counters[i] = repl->counters[mappos];
+			break;
+
+		case COUNTER_MAP_ZEROED:
+			/* Original read: X.
+			 * Atomic read on replacement: X + Y.
+			 * Currently in kernel: Z.
+			 * Want in kernel: Y + Z.
+			 * => Add in Y.
+			 * => Add in (replacement read - original read).
+			 */
+			subtract_counters(&newcounters->counters[i],
+					  &repl->counters[mappos],
+					  &index2entry(*handle, i)->counters);
+			break;
+
+		case COUNTER_MAP_SET:
+			/* Want to set counter (iptables-restore) */
+
+			memcpy(&newcounters->counters[i],
+			       &index2entry(*handle, i)->counters,
+			       sizeof(STRUCT_COUNTERS));
+
+			break;
+		}
+	}
+
+#ifdef KERNEL_64_USERSPACE_32
+	{
+		/* Kernel will think that pointer should be 64-bits, and get
+		   padding.  So we accomodate here (assumption: alignment of
+		   `counters' is on 64-bit boundary). */
+		u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+		if ((unsigned long)&newcounters->counters % 8 != 0) {
+			fprintf(stderr,
+				"counters alignment incorrect! Mail rusty!\n");
+			abort();
+		}
+		*kernptr = newcounters->counters;
+	}
+#endif /* KERNEL_64_USERSPACE_32 */
+
+	if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+		       newcounters, counterlen) < 0) {
+		free(repl->counters);
+		free(repl);
+		free(newcounters);
+		return 0;
+	}
+
+	free(repl->counters);
+	free(repl);
+	free(newcounters);
+
+ finished:
+	if ((*handle)->cache_chain_heads)
+		free((*handle)->cache_chain_heads);
+	free(*handle);
+	*handle = NULL;
+	return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+	return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+	unsigned int i;
+	struct table_struct {
+		void *fn;
+		int err;
+		const char *message;
+	} table [] =
+	  { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+	    { TC_INIT, EINVAL, "Module is wrong version" },
+	    { TC_INIT, ENOENT, 
+		    "Table does not exist (do you need to insmod?)" },
+	    { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+	    { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+	    { TC_DELETE_CHAIN, EMLINK,
+	      "Can't delete chain with references left" },
+	    { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+	    { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+	    { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+	    { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+	    { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+	    { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+	    /* EINVAL for CHECK probably means bad interface. */
+	    { TC_CHECK_PACKET, EINVAL,
+	      "Bad arguments (does that interface exist?)" },
+	    { TC_CHECK_PACKET, ENOSYS,
+	      "Checking will most likely never get implemented" },
+	    /* ENOENT for DELETE probably means no matching rule */
+	    { TC_DELETE_ENTRY, ENOENT,
+	      "Bad rule (does a matching rule exist in that chain?)" },
+	    { TC_SET_POLICY, ENOENT,
+	      "Bad built-in chain name" },
+	    { TC_SET_POLICY, EINVAL,
+	      "Bad policy name" },
+
+	    { NULL, 0, "Incompatible with this kernel" },
+	    { NULL, ENOPROTOOPT, "arptables who? (do you need to insmod?)" },
+	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+	    { NULL, ENOMEM, "Memory allocation problem" },
+	    { NULL, ENOENT, "No chain/target/match by that name" },
+	  };
+
+	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+		if ((!table[i].fn || table[i].fn == arptc_fn)
+		    && table[i].err == err)
+			return table[i].message;
+	}
+
+	return strerror(err);
+}
diff --git a/userspace/ebtables2/COPYING b/userspace/ebtables2/COPYING
new file mode 100644
index 0000000..514754e
--- /dev/null
+++ b/userspace/ebtables2/COPYING
@@ -0,0 +1,342 @@
+All code in this package, including the code from the extensions,
+is released under the GPL license, which you find hereafter.
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/userspace/ebtables2/ChangeLog b/userspace/ebtables2/ChangeLog
new file mode 100644
index 0000000..52de4db
--- /dev/null
+++ b/userspace/ebtables2/ChangeLog
@@ -0,0 +1,209 @@
+20111215
+	Changelog for v2.0.10-4
+	* really fix counter setting bug (thanks to James' persistence)
+20111204
+	Changelog for v2.0.10-3
+	* fix counter setting bug (reported by James Sinclair)
+20110710
+	Changelog for v2.0.10-2
+	* enable compiler optimizations (-O3)
+	* small changes to remove the compiler warnings due to optimization being
+	  turned on (thanks to Peter Volkov)
+	* respect LDFLAGS in Makefiles (Peter Volkov)
+20110710
+	Changelog for v2.0.10-1
+	* fix --among-dst-file, which translated to --among-src
+	  (reported by Thierry Watelet)
+	* fix bug in test_ulog.c example
+	* Makefile: respect LDFLAGS during ebtables build (Peter Volkov)
+	* Makefile: create directories to avoid build failure when DESTDIR is
+	  supplied (Peter Volkov)
+	* incorporate fixes for possible issues found by Coverity analysis
+	  (thanks to Jiri Popelka)
+	* define __EXPORTED_HEADERS__ to get access to the Linux kernel headers
+	* extend ebt_ip6 to allow matching on ipv6-icmp types/codes (by Florian
+	  Westphal)
+	* Print a more useful error message when an update of the kernel table
+	  failed.
+	* Add --concurrent option, which enables using a file lock to support
+	  concurrent scripts updating the ebtables kernel tables
+20100203
+	Changelog for v2.0.9-2
+	* fix unwanted zeroing of counters in the last user-defined chain
+	  (reported by Jon Lewis)
+	* fix hidden symbol compilation error when using ld directly
+	* fix return value checking of creat to give a correct error
+	  message if the atomic file couldn't be created
+	* correct info in INSTALL about compilation of ulog
+20090621
+	Changelog for v2.0.9 vs v2.0.8-2
+	* added ip6 module for filtering IPv6 traffic (Kuo-Lang Tseng,
+	  Manohar Castelino)
+	* added --log-ip6 option for logging IPv6 traffic (Kuo-Lang Tseng,
+	  Manohar Castelino)
+	* added nflog watcher for logging packets to userspace (Peter Warasin)
+	* bugfix in ebtables.sysv (Michal Soltys)
+	* bugfix for among match on x86-64 (reported by Pavel Emelyanov)
+20061217
+	Since last entry:
+	* fixed a few reported bugs
+	* ebt_among --among-dst-file and --among-src-file: allow
+	  the list to be given in a file (circumvents command line max.
+	  line length
+	* ebt_nat --snat-arp: if it's an arp packet, also change the source
+	  address in the arp header
+	* ebt_mark --mark-or, --mark-xor, --mark-and
+20051020
+	Since last entry:
+	* ebtables modules are now located in /usr/lib/ebtables/
+	* added '/sbin/service ebtables' support
+	* added ebtables-save (thanks to Rok Papez <rok.papez@arnes.si>)
+	  and ebtables-restore (the first one a perl script, the second
+	  one written in c (fast))
+	* optimized the code for the '-A' command, making ebtables-restore
+	  very fast.
+	* ebtablesd/ebtablesu is deprecated and not compiled by default
+	  the ebtables-save/ebtables-restore scheme is much better
+20050117
+	Since last entry:
+	* added ulog watcher
+	* made the ebtables code modular (make library functions).
+	* added the ebtablesd/ebtablesu scheme to allow faster
+	  addition of rules (and to test the modular code).
+	* some small fixes
+	* added -c option (initialize counters)
+	* added -C option (change counters)
+20031102
+	Since last entry:
+	* <grzes_at_gnu.univ.gda.pl> added arpreply and among modules
+	* <tommy_at_home.tig-grr.com> added limit match
+20030724
+	* added (automatic) Sparc64 support, thanks to Michael Bellion and
+	  Thomas Heinz from hipac.org for providing a test-box.
+20030717
+	* added stp frames match type
+20030713
+	* added support for deleting all user-defined chains (-X option
+	  without specified chain)
+20030601
+	* added --Lmac2
+	* <csv_at_bluetail.com> Chris Vitale: basic 802.3/802.2 filtering
+	  (experimental, kernel files are in the CVS)
+
+20030503
+	* added negative rule counter support
+	* bugfix: bcnt was not updated correctly
+	* <blancher_at_cartel-securite.fr> Cedric Blancher: add ARP MAC
+	  matching support
+	* added pkttype match
+20030402
+	* fixed check bug in ebt_ip.c (report from
+	  joe_judge_at_guardium.com).
+20030111
+	* fixed problem when removing a chain (report from
+	  ykphuah_at_greenpacket.com).
+	* Added --help list_extensions which, well, lists the extensions
+20021203
+	* changed the way to use the atomic operations. It's now possible
+	  to use the EBTABLES_ATOMIC_FILE environment variable, so it's no
+	  longer necessary to explicitly state the file name. See the man.
+20021120
+	* changed the way of compiling. New releases will now contain their
+	  own set of kernel includes. No more copying of kernel includes to
+	  /usr/include/linux
+	* added getethertype.c (Nick) and use it. Removed name_to_number()
+	  and number_to_name().
+20021106
+	* added possibility to specify a rule number interval when deleting
+	  rules
+20021102
+	* added ! - option possibility, which is equivalent to - ! option
+20021102
+	* since last entry: added byte counters and udp/tcp port matching
+20020830
+	* updated the kernel files for 2.4.20-pre5 and 2.5.32
+	* last big cleanup of kernel and userspace code just finished
+20020820
+	* ARP module bugfix
+	* IP module bugfix
+	* nat module bugfix
+20020730
+	* other things done before 2.0-rc1 that I can think of,
+	  including kernel:
+	* cache align counters for better smp performance
+	* simplify snat code
+	* check for --xxxx-target RETURN on base chain
+	* cleanup code
+	* minor bugfixes
+20020724
+	* code cleanup
+	* bugfix for --atomic-commit
+20020720
+	* added mark target+match
+20020714
+	* added --atomic options
+20020710
+	* some unlogged changes (due to lazyness)
+	* added --Lc, --Ln, --Lx
+20020625
+	* user defined chains support: added -N, -X, -E options.
+20020621
+	* some unlogged changes (due to lazyness)
+	* change the output for -L to make it look like it would look when
+	  the user inputs the command.
+	* try to autoload modules
+	* some minor bugfixes
+	* add user defined chains support (without new commands yet,
+	  deliberately)
+	* comparing rules didn't take the logical devices into account
+20020520
+	* update help for -s and -d
+	* add VLAN in ethertypes
+	* add SYMLINK option for compiling
+20020501
+	* allow -i and --logical-in in BROUTING
+	* update the manual page
+	* rename /etc/etherproto into /etc/ethertypes (seems to be a more
+	  standard name)
+	* add MAC mask for -s and -d, also added Unicast, Multicast and
+	  Broadcast specification for specifying a (family of) MAC
+	  addresses.
+20020427
+	* added broute table.
+	* added redirect target.
+	* added --redirect-target, --snat-target and --dnat-target options.
+	* added logical_out and logical_in
+	* snat bugfix (->size)
+20020414
+	* fixed some things in the manual.
+	* fixed -P problem.
+20020411
+	* -j standard no longer works, is this cryptic? good :)
+	* lots of beautification.
+	  - made some code smaller
+	  - made everything fit within 80 columns
+	* fix problems with -i and -o option
+	* print_memory now prints useful info
+	* trying to see the tables when ebtables is not loaded in kernel
+	  no longer makes this be seen as a bug.
+20020403
+	ebtables v2.0 released, changes:
+	* A complete rewrite, made everything modular.
+	* Fixed a one year old bug in br_db.c. A similar bug was present
+	  in ebtables.c. It was visible when the number of rules got
+	  bigger (around 90).
+	* Removed the option to allow/disallow counters. Frames passing
+	  by are always counted now.
+	* Didn't really add any new functionality. However, it will be
+	  _alot_ easier and prettier to do so now. Feel free to add an
+	  extension yourself.
+	* There are 4 types of extensions:
+	  - Tables.
+	  - Matches: like iptables has.
+	  - Watchers: these only watch frames that passed all the matches
+	    of the rule. They don't change the frame, nor give a verdict.
+	    The log extension is a watcher.
+	  - Targets.
+	* user32/kernel64 architectures like the Sparc64 are unsupported.
+	  If you want me to change this, give me access to such a box,
+	  and don't pressure me.
diff --git a/userspace/ebtables2/INSTALL b/userspace/ebtables2/INSTALL
new file mode 100644
index 0000000..4a05c67
--- /dev/null
+++ b/userspace/ebtables2/INSTALL
@@ -0,0 +1,61 @@
+FOLLOW THESE SIMPLE GUIDELINES:
+-------------------------------
+
+Compiling the source code:
+%make
+Put the files in the right directories:
+%make install
+
+If you are using the CVS code or need your own kernel includes, do this
+instead (change the include directory to the appropriate one):
+%make install KERNEL_INCLUDES=/usr/src/linux/include
+
+If you want to make a static binary for ebtables, containing all the
+extensions, without shared libraries, do this (this will make a
+binary called 'static', which you can rename):
+%make static
+
+WHAT GETS INSTALLED AND WHAT OPTIONS ARE AVAILABLE?
+---------------------------------------------------
+
+- The ebtables manual gets installed in /usr/local/man/man8
+  To put the manual somewhere else, include MANDIR=<<man-path/man>> as
+  option on the command line.
+  The Makefile will append /man8/ebtables.8.
+- ethertypes is by default placed in /etc/, if you
+  want to change this, include ETHERTYPESPATH=<<path>>.
+- The userspace programs ebtables ebtables-save and ebtables-restore are
+  are copied by default to /usr/local/sbin/ebtables. If you want to put
+  the executables somewhere else, include BINPATH=<<path>>.
+- The ebtables initialisation file (enabling use of 'service ebtables') is
+  copied to /etc/rc.d/init.d (change with option INITDIR)
+- The ebtables configuration file (ebtables-config) is copied to /etc/sysconfig
+- ebtables can use a lock file to enable concurrent execution of the ebtables
+  tool. The standard location of the lock file is /var/lib/ebtables/lock.
+  Include LOCKFILE=<<path-to-file>> if you want to use another file.
+
+That's all
+
+You can also use a base directory different from the root directory (/),
+using the DESTDIR option. See the Makefile for more details.
+
+
+ADDITIONAL PROGRAMS:
+----------------------
+-- examples/ulog/test_ulog.c --
+
+Contains an example to receive and parse netlink messages containing
+packets seen by the ebtables ulog watcher.
+
+Compile with:
+%make test_ulog KERNEL_INCLUDES=/usr/src/linux/include
+
+Usage:
+%examples/ulog/test_ulog NETLINK_GROUP
+%ebtables -A chain --ulog-nlgroup NETLINK_GROUP
+
+-- examples/perf_test/perf_test --
+
+A test script to compare the performance for the different ways to
+construct an ebtables table. This is deprecated and should probably
+be ignored.
diff --git a/userspace/ebtables2/Makefile b/userspace/ebtables2/Makefile
new file mode 100644
index 0000000..c1106a4
--- /dev/null
+++ b/userspace/ebtables2/Makefile
@@ -0,0 +1,246 @@
+# ebtables Makefile
+
+PROGNAME:=ebtables
+PROGRELEASE:=4
+PROGVERSION_:=2.0.10
+PROGVERSION:=$(PROGVERSION_)-$(PROGRELEASE)
+PROGDATE:=December\ 2011
+LOCKFILE?=/var/lib/ebtables/lock
+LOCKDIR:=$(shell echo $(LOCKFILE) | sed 's/\(.*\)\/.*/\1/')/
+
+# default paths
+LIBDIR:=/usr/lib
+MANDIR:=/usr/local/man
+BINDIR:=/usr/local/sbin
+ETCDIR:=/etc
+INITDIR:=/etc/rc.d/init.d
+SYSCONFIGDIR:=/etc/sysconfig
+DESTDIR:=
+
+CFLAGS:=-Wall -Wunused -Werror
+CFLAGS_SH_LIB:=-fPIC -O3
+CC:=gcc
+
+ifeq ($(shell uname -m),sparc64)
+CFLAGS+=-DEBT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+include extensions/Makefile
+
+OBJECTS2:=getethertype.o communication.o libebtc.o \
+useful_functions.o ebtables.o
+
+OBJECTS:=$(OBJECTS2) $(EXT_OBJS) $(EXT_LIBS)
+
+KERNEL_INCLUDES?=include/
+
+ETHERTYPESPATH?=$(ETCDIR)
+ETHERTYPESFILE:=$(ETHERTYPESPATH)/ethertypes
+
+PIPE_DIR?=/tmp/$(PROGNAME)-v$(PROGVERSION)
+PIPE=$(PIPE_DIR)/ebtablesd_pipe
+EBTD_CMDLINE_MAXLN?=2048
+EBTD_ARGC_MAX?=50
+
+PROGSPECS:=-DPROGVERSION=\"$(PROGVERSION)\" \
+	-DPROGNAME=\"$(PROGNAME)\" \
+	-DPROGDATE=\"$(PROGDATE)\" \
+	-D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\" \
+	-DEBTD_ARGC_MAX=$(EBTD_ARGC_MAX) \
+	-DEBTD_CMDLINE_MAXLN=$(EBTD_CMDLINE_MAXLN) \
+	-DLOCKFILE=\"$(LOCKFILE)\" \
+	-DLOCKDIR=\"$(LOCKDIR)\"
+
+# You can probably ignore this, ebtables{u,d} are normally not used
+PROGSPECSD:=-DPROGVERSION=\"$(PROGVERSION)\" \
+	-DPROGNAME=\"$(PROGNAME)\" \
+	-DPROGDATE=\"$(PROGDATE)\" \
+	-D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\" \
+	-DEBTD_CMDLINE_MAXLN=$(EBTD_CMDLINE_MAXLN) \
+	-DEBTD_ARGC_MAX=$(EBTD_ARGC_MAX) \
+	-DEBTD_PIPE=\"$(PIPE)\" \
+	-DEBTD_PIPE_DIR=\"$(PIPE_DIR)\"
+
+# Uncomment for debugging (slower)
+#PROGSPECS+=-DEBT_DEBUG
+#PROGSPECSD+=-DEBT_DEBUG
+#CFLAGS+=-ggdb
+
+all: ebtables ebtables-restore
+
+communication.o: communication.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
+libebtc.o: libebtc.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
+useful_functions.o: useful_functions.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
+getethertype.o: getethertype.c include/ethernetdb.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -Iinclude/
+
+ebtables.o: ebtables.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
+ebtables-standalone.o: ebtables-standalone.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c $< -o $@ -I$(KERNEL_INCLUDES)
+
+libebtc.so: $(OBJECTS2)
+	$(CC) -shared $(LDFLAGS) -Wl,-soname,libebtc.so -o libebtc.so -lc $(OBJECTS2)
+
+ebtables: $(OBJECTS) ebtables-standalone.o libebtc.so
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(LDFLAGS) -o $@ ebtables-standalone.o -I$(KERNEL_INCLUDES) -L. -Lextensions -lebtc $(EXT_LIBSI) \
+	-Wl,-rpath,$(LIBDIR)
+
+ebtablesu: ebtablesu.c
+	$(CC) $(CFLAGS) $(PROGSPECSD) $< -o $@
+
+ebtablesd.o: ebtablesd.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(PROGSPECSD) -c $< -o $@  -I$(KERNEL_INCLUDES)
+
+ebtablesd: $(OBJECTS) ebtablesd.o libebtc.so
+	$(CC) $(CFLAGS) -o $@ ebtablesd.o -I$(KERNEL_INCLUDES) -L. -Lextensions -lebtc $(EXT_LIBSI) \
+	-Wl,-rpath,$(LIBDIR)
+
+ebtables-restore.o: ebtables-restore.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(PROGSPECS) -c $< -o $@  -I$(KERNEL_INCLUDES)
+
+ebtables-restore: $(OBJECTS) ebtables-restore.o libebtc.so
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ ebtables-restore.o -I$(KERNEL_INCLUDES) -L. -Lextensions -lebtc $(EXT_LIBSI) \
+	-Wl,-rpath,$(LIBDIR)
+
+.PHONY: daemon
+daemon: ebtablesd ebtablesu
+
+# a little scripting for a static binary, making one for ebtables-restore
+# should be completely analogous
+static: extensions/ebt_*.c extensions/ebtable_*.c ebtables.c communication.c ebtables-standalone.c getethertype.c libebtc.c useful_functions.c
+	cp ebtables-standalone.c ebtables-standalone.c_ ; \
+	cp include/ebtables_u.h include/ebtables_u.h_ ; \
+	sed "s/ main(/ pseudomain(/" ebtables-standalone.c > ebtables-standalone.c__ ; \
+	mv ebtables-standalone.c__ ebtables-standalone.c ; \
+	printf "\nint main(int argc, char *argv[])\n{\n "  >> ebtables-standalone.c ; \
+	for arg in $(EXT_FUNC) \
+	; do \
+	sed s/_init/_$${arg}_init/ extensions/ebt_$${arg}.c > extensions/ebt_$${arg}.c_ ; \
+	mv extensions/ebt_$${arg}.c_ extensions/ebt_$${arg}.c ; \
+	printf "\t%s();\n" _$${arg}_init >> ebtables-standalone.c ; \
+	printf "extern void %s();\n" _$${arg}_init >> include/ebtables_u.h ; \
+	done ; \
+	for arg in $(EXT_TABLES) \
+	; do \
+	sed s/_init/_t_$${arg}_init/ extensions/ebtable_$${arg}.c > extensions/ebtable_$${arg}.c_ ; \
+	mv extensions/ebtable_$${arg}.c_ extensions/ebtable_$${arg}.c ; \
+	printf "\t%s();\n" _t_$${arg}_init >> ebtables-standalone.c ; \
+	printf "extern void %s();\n" _t_$${arg}_init >> include/ebtables_u.h ; \
+	done ; \
+	printf "\n\tpseudomain(argc, argv);\n\treturn 0;\n}\n" >> ebtables-standalone.c ;\
+	$(CC) $(CFLAGS) $(LDFLAGS) $(PROGSPECS) -o $@ $^ -I$(KERNEL_INCLUDES) -Iinclude ; \
+	for arg in $(EXT_FUNC) \
+	; do \
+	sed "s/ .*_init/ _init/" extensions/ebt_$${arg}.c > extensions/ebt_$${arg}.c_ ; \
+	mv extensions/ebt_$${arg}.c_ extensions/ebt_$${arg}.c ; \
+	done ; \
+	for arg in $(EXT_TABLES) \
+	; do \
+	sed "s/ .*_init/ _init/" extensions/ebtable_$${arg}.c > extensions/ebtable_$${arg}.c_ ; \
+	mv extensions/ebtable_$${arg}.c_ extensions/ebtable_$${arg}.c ; \
+	done ; \
+	mv ebtables-standalone.c_ ebtables-standalone.c ; \
+	mv include/ebtables_u.h_ include/ebtables_u.h
+
+tmp1:=$(shell printf $(BINDIR) | sed 's/\//\\\//g')
+tmp2:=$(shell printf $(SYSCONFIGDIR) | sed 's/\//\\\//g')
+tmp3:=$(shell printf $(PIPE) | sed 's/\//\\\//g')
+.PHONY: scripts
+scripts: ebtables-save ebtables.sysv ebtables-config
+	cat ebtables-save | sed 's/__EXEC_PATH__/$(tmp1)/g' > ebtables-save_
+	mkdir -p $(DESTDIR)$(BINDIR)
+	install -m 0755 -o root -g root ebtables-save_ $(DESTDIR)$(BINDIR)/ebtables-save
+	cat ebtables.sysv | sed 's/__EXEC_PATH__/$(tmp1)/g' | sed 's/__SYSCONFIG__/$(tmp2)/g' > ebtables.sysv_
+	if [ "$(DESTDIR)" != "" ]; then mkdir -p $(DESTDIR)$(INITDIR); fi
+	if test -d $(DESTDIR)$(INITDIR); then install -m 0755 -o root -g root ebtables.sysv_ $(DESTDIR)$(INITDIR)/ebtables; fi
+	cat ebtables-config | sed 's/__SYSCONFIG__/$(tmp2)/g' > ebtables-config_
+	if [ "$(DESTDIR)" != "" ]; then mkdir -p $(DESTDIR)$(SYSCONFIGDIR); fi
+	if test -d $(DESTDIR)$(SYSCONFIGDIR); then install -m 0600 -o root -g root ebtables-config_ $(DESTDIR)$(SYSCONFIGDIR)/ebtables-config; fi
+	rm -f ebtables-save_ ebtables.sysv_ ebtables-config_
+
+tmp4:=$(shell printf $(LOCKFILE) | sed 's/\//\\\//g')
+$(MANDIR)/man8/ebtables.8: ebtables.8
+	mkdir -p $(DESTDIR)$(@D)
+	sed -e 's/$$(VERSION)/$(PROGVERSION)/' -e 's/$$(DATE)/$(PROGDATE)/' -e 's/$$(LOCKFILE)/$(tmp4)/' ebtables.8 > ebtables.8_
+	install -m 0644 -o root -g root ebtables.8_ $(DESTDIR)$@
+	rm -f ebtables.8_
+
+$(DESTDIR)$(ETHERTYPESFILE): ethertypes
+	mkdir -p $(@D)
+	install -m 0644 -o root -g root $< $@
+
+.PHONY: exec
+exec: ebtables ebtables-restore
+	mkdir -p $(DESTDIR)$(BINDIR)
+	install -m 0755 -o root -g root $(PROGNAME) $(DESTDIR)$(BINDIR)/$(PROGNAME)
+	install -m 0755 -o root -g root ebtables-restore $(DESTDIR)$(BINDIR)/ebtables-restore
+
+.PHONY: install
+install: $(MANDIR)/man8/ebtables.8 $(DESTDIR)$(ETHERTYPESFILE) exec scripts
+	mkdir -p $(DESTDIR)$(LIBDIR)
+	install -m 0755 extensions/*.so $(DESTDIR)$(LIBDIR)
+	install -m 0755 *.so $(DESTDIR)$(LIBDIR)
+
+.PHONY: clean
+clean:
+	rm -f ebtables ebtables-restore ebtablesd ebtablesu static
+	rm -f *.o *~ *.so
+	rm -f extensions/*.o extensions/*.c~ extensions/*.so include/*~
+
+DIR:=$(PROGNAME)-v$(PROGVERSION)
+CVSDIRS:=CVS extensions/CVS examples/CVS examples/perf_test/CVS \
+examples/ulog/CVS include/CVS
+# This is used to make a new userspace release, some files are altered so
+# do this on a temporary version
+.PHONY: release
+release:
+	rm -f extensions/ebt_inat.c
+	rm -rf $(CVSDIRS)
+	mkdir -p include/linux/netfilter_bridge
+	install -m 0644 -o root -g root \
+		$(KERNEL_INCLUDES)/linux/netfilter_bridge.h include/linux/
+# To keep possible compile error complaints about undefined ETH_P_8021Q
+# off my back
+	install -m 0644 -o root -g root \
+		$(KERNEL_INCLUDES)/linux/if_ether.h include/linux/
+	install -m 0644 -o root -g root \
+		$(KERNEL_INCLUDES)/linux/types.h include/linux/
+	install -m 0644 -o root -g root \
+		$(KERNEL_INCLUDES)/linux/netfilter_bridge/*.h \
+		include/linux/netfilter_bridge/
+	install -m 0644 -o root -g root \
+		include/ebtables.h include/linux/netfilter_bridge/
+	make clean
+	touch *
+	touch extensions/*
+	touch include/*
+	touch include/linux/*
+	touch include/linux/netfilter_bridge/*
+	sed -i -e 's/$$(VERSION)/$(PROGVERSION)/' -e 's/$$(DATE)/$(PROGDATE)/' -e 's/$$(LOCKFILE)/$(tmp4)/' ebtables.8
+	sed -i -e 's/$$(VERSION)/$(PROGVERSION_)/' -e 's/$$(RELEASE)/$(PROGRELEASE)/' ebtables.spec
+	cd ..;tar -c $(DIR) | gzip >$(DIR).tar.gz; cd -
+	rm -rf include/linux
+
+# This will make the rpm and put it in /usr/src/redhat/RPMS
+# (do this as root after make release)
+.PHONY: rpmbuild
+rpmbuild:
+	cp ../$(DIR).tar.gz /usr/src/redhat/SOURCES/
+	rpmbuild --buildroot $(shell mktemp -td $(DIR)-XXXXX) -bb ebtables.spec
+
+.PHONY: test_ulog
+test_ulog: examples/ulog/test_ulog.c getethertype.o
+	$(CC) $(CFLAGS)  $< -o test_ulog -I$(KERNEL_INCLUDES) -lc \
+	getethertype.o
+	mv test_ulog examples/ulog/
+
+.PHONY: examples
+examples: test_ulog
diff --git a/userspace/ebtables2/THANKS b/userspace/ebtables2/THANKS
new file mode 100644
index 0000000..4b92ead
--- /dev/null
+++ b/userspace/ebtables2/THANKS
@@ -0,0 +1,9 @@
+Special thanks go out to these early contributors:
+
+Lennert Buytenhek
+Rusty Russel
+Harald Welte
+Jason Lunz
+Tim Gardner
+Loïc Minier
+Nick Fedchik
diff --git a/userspace/ebtables2/communication.c b/userspace/ebtables2/communication.c
new file mode 100644
index 0000000..62ed667
--- /dev/null
+++ b/userspace/ebtables2/communication.c
@@ -0,0 +1,772 @@
+/*
+ * communication.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ */
+
+/*
+ * All the userspace/kernel communication is in this file.
+ * The other code should not have to know anything about the way the
+ * kernel likes the structure of the table data.
+ * The other code works with linked lists. So, the translation is done here.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "include/ebtables_u.h"
+
+extern char* hooknames[NF_BR_NUMHOOKS];
+
+#ifdef KERNEL_64_USERSPACE_32
+#define sparc_cast (uint64_t)
+#else
+#define sparc_cast
+#endif
+
+int sockfd = -1;
+
+static int get_sockfd()
+{
+	int ret = 0;
+	if (sockfd == -1) {
+		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+		if (sockfd < 0) {
+			ebt_print_error("Problem getting a socket, "
+					"you probably don't have the right "
+					"permissions");
+			ret = -1;
+		}
+	}
+	return ret;
+}
+
+static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl)
+{
+	struct ebt_replace *new;
+	struct ebt_u_entry *e;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries;
+	char *p, *base;
+	int i, j;
+	unsigned int entries_size = 0, *chain_offsets;
+
+	new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+	if (!new)
+		ebt_print_memory();
+	new->valid_hooks = u_repl->valid_hooks;
+	strcpy(new->name, u_repl->name);
+	new->nentries = u_repl->nentries;
+	new->num_counters = u_repl->num_counters;
+	new->counters = sparc_cast u_repl->counters;
+	chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int));
+	if (!chain_offsets)
+		ebt_print_memory();
+	/* Determine size */
+	for (i = 0; i < u_repl->num_chains; i++) {
+		if (!(entries = u_repl->chains[i]))
+			continue;
+		chain_offsets[i] = entries_size;
+		entries_size += sizeof(struct ebt_entries);
+		j = 0;
+		e = entries->entries->next;
+		while (e != entries->entries) {
+			j++;
+			entries_size += sizeof(struct ebt_entry);
+			m_l = e->m_list;
+			while (m_l) {
+				entries_size += m_l->m->match_size +
+				   sizeof(struct ebt_entry_match);
+				m_l = m_l->next;
+			}
+			w_l = e->w_list;
+			while (w_l) {
+				entries_size += w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher);
+				w_l = w_l->next;
+			}
+			entries_size += e->t->target_size +
+			   sizeof(struct ebt_entry_target);
+			e = e->next;
+		}
+		/* A little sanity check */
+		if (j != entries->nentries)
+			ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j,
+			   entries->nentries, entries->name);
+	}
+
+	new->entries_size = entries_size;
+	p = (char *)malloc(entries_size);
+	if (!p)
+		ebt_print_memory();
+
+	/* Put everything in one block */
+	new->entries = sparc_cast p;
+	for (i = 0; i < u_repl->num_chains; i++) {
+		struct ebt_entries *hlp;
+
+		hlp = (struct ebt_entries *)p;
+		if (!(entries = u_repl->chains[i]))
+			continue;
+		if (i < NF_BR_NUMHOOKS)
+			new->hook_entry[i] = sparc_cast hlp;
+		hlp->nentries = entries->nentries;
+		hlp->policy = entries->policy;
+		strcpy(hlp->name, entries->name);
+		hlp->counter_offset = entries->counter_offset;
+		hlp->distinguisher = 0; /* Make the kernel see the light */
+		p += sizeof(struct ebt_entries);
+		e = entries->entries->next;
+		while (e != entries->entries) {
+			struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+			tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
+			tmp->invflags = e->invflags;
+			tmp->ethproto = e->ethproto;
+			strcpy(tmp->in, e->in);
+			strcpy(tmp->out, e->out);
+			strcpy(tmp->logical_in, e->logical_in);
+			strcpy(tmp->logical_out, e->logical_out);
+			memcpy(tmp->sourcemac, e->sourcemac,
+			   sizeof(tmp->sourcemac));
+			memcpy(tmp->sourcemsk, e->sourcemsk,
+			   sizeof(tmp->sourcemsk));
+			memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
+			memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
+
+			base = p;
+			p += sizeof(struct ebt_entry);
+			m_l = e->m_list;
+			while (m_l) {
+				memcpy(p, m_l->m, m_l->m->match_size +
+				   sizeof(struct ebt_entry_match));
+				p += m_l->m->match_size +
+				   sizeof(struct ebt_entry_match);
+				m_l = m_l->next;
+			}
+			tmp->watchers_offset = p - base;
+			w_l = e->w_list;
+			while (w_l) {
+				memcpy(p, w_l->w, w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher));
+				p += w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher);
+				w_l = w_l->next;
+			}
+			tmp->target_offset = p - base;
+			memcpy(p, e->t, e->t->target_size +
+			   sizeof(struct ebt_entry_target));
+			if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+				struct ebt_standard_target *st =
+				   (struct ebt_standard_target *)p;
+				/* Translate the jump to a udc */
+				if (st->verdict >= 0)
+					st->verdict = chain_offsets
+					   [st->verdict + NF_BR_NUMHOOKS];
+			}
+			p += e->t->target_size +
+			   sizeof(struct ebt_entry_target);
+			tmp->next_offset = p - base;
+			e = e->next;
+		}
+	}
+
+	/* Sanity check */
+	if (p - (char *)new->entries != new->entries_size)
+		ebt_print_bug("Entries_size bug");
+	free(chain_offsets);
+	return new;
+}
+
+static void store_table_in_file(char *filename, struct ebt_replace *repl)
+{
+	char *data;
+	int size;
+	int fd;
+
+	/* Start from an empty file with the correct priviliges */
+	if ((fd = creat(filename, 0600)) == -1) {
+		ebt_print_error("Couldn't create file %s", filename);
+		return;
+	}
+
+	size = sizeof(struct ebt_replace) + repl->entries_size +
+	   repl->nentries * sizeof(struct ebt_counter);
+	data = (char *)malloc(size);
+	if (!data)
+		ebt_print_memory();
+	memcpy(data, repl, sizeof(struct ebt_replace));
+	memcpy(data + sizeof(struct ebt_replace), (char *)repl->entries,
+	   repl->entries_size);
+	/* Initialize counters to zero, deliver_counters() can update them */
+	memset(data + sizeof(struct ebt_replace) + repl->entries_size,
+	   0, repl->nentries * sizeof(struct ebt_counter));
+	if (write(fd, data, size) != size)
+		ebt_print_error("Couldn't write everything to file %s",
+				filename);
+	close(fd);
+	free(data);
+}
+
+void ebt_deliver_table(struct ebt_u_replace *u_repl)
+{
+	socklen_t optlen;
+	struct ebt_replace *repl;
+
+	/* Translate the struct ebt_u_replace to a struct ebt_replace */
+	repl = translate_user2kernel(u_repl);
+	if (u_repl->filename != NULL) {
+		store_table_in_file(u_repl->filename, repl);
+		goto free_repl;
+	}
+	/* Give the data to the kernel */
+	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+	if (get_sockfd())
+		goto free_repl;
+	if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+		goto free_repl;
+	if (u_repl->command == 8) { /* The ebtables module may not
+	                             * yet be loaded with --atomic-commit */
+		ebtables_insmod("ebtables");
+		if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES,
+		    repl, optlen))
+			goto free_repl;
+	}
+
+	ebt_print_error("Unable to update the kernel. Two possible causes:\n"
+			"1. Multiple ebtables programs were executing simultaneously. The ebtables\n"
+			"   userspace tool doesn't by default support multiple ebtables programs running\n"
+			"   concurrently. The ebtables option --concurrent or a tool like flock can be\n"
+			"   used to support concurrent scripts that update the ebtables kernel tables.\n"
+			"2. The kernel doesn't support a certain ebtables extension, consider\n"
+			"   recompiling your kernel or insmod the extension.\n");
+free_repl:
+	if (repl) {
+		free(repl->entries);
+		free(repl);
+	}
+}
+
+static int store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+{
+	int size = repl->nentries * sizeof(struct ebt_counter), ret = 0;
+	unsigned int entries_size;
+	struct ebt_replace hlp;
+	FILE *file;
+
+	if (!(file = fopen(filename, "r+b"))) {
+		ebt_print_error("Could not open file %s", filename);
+		return -1;
+	}
+	/* Find out entries_size and then set the file pointer to the
+	 * counters */
+	if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
+	   || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
+	   sizeof(unsigned int) ||
+	   fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) {
+		ebt_print_error("File %s is corrupt", filename);
+		ret = -1;
+		goto close_file;
+	}
+	if (fwrite(repl->counters, sizeof(char), size, file) != size) {
+		ebt_print_error("Could not write everything to file %s",
+				filename);
+		ret = -1;
+	}
+close_file:
+	fclose(file);
+	return 0;
+}
+
+/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel
+ * and resets the counterchanges to CNT_NORM */
+void ebt_deliver_counters(struct ebt_u_replace *u_repl)
+{
+	struct ebt_counter *old, *new, *newcounters;
+	socklen_t optlen;
+	struct ebt_replace repl;
+	struct ebt_cntchanges *cc = u_repl->cc->next, *cc2;
+	struct ebt_u_entries *entries = NULL;
+	struct ebt_u_entry *next = NULL;
+	int i, chainnr = -1;
+
+	if (u_repl->nentries == 0)
+		return;
+
+	newcounters = (struct ebt_counter *)
+	   malloc(u_repl->nentries * sizeof(struct ebt_counter));
+	if (!newcounters)
+		ebt_print_memory();
+	memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
+	old = u_repl->counters;
+	new = newcounters;
+	while (cc != u_repl->cc) {
+		if (!next || next == entries->entries) {
+			chainnr++;
+			while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) ||
+			       (next = entries->entries->next) == entries->entries))
+				chainnr++;
+			if (chainnr == u_repl->num_chains)
+				break;
+		}
+		if (next == NULL)
+			ebt_print_bug("next == NULL");
+		if (cc->type == CNT_NORM) {
+			/* 'Normal' rule, meaning we didn't do anything to it
+			 * So, we just copy */
+			*new = *old;
+			next->cnt = *new;
+			next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+			old++; /* We've used an old counter */
+			new++; /* We've set a new counter */
+			next = next->next;
+		} else if (cc->type == CNT_DEL) {
+			old++; /* Don't use this old counter */
+		} else {
+			if (cc->type == CNT_CHANGE) {
+				if (cc->change % 3 == 1)
+					new->pcnt = old->pcnt + next->cnt_surplus.pcnt;
+				else if (cc->change % 3 == 2)
+					new->pcnt = old->pcnt - next->cnt_surplus.pcnt;
+				else
+					new->pcnt = next->cnt.pcnt;
+				if (cc->change / 3 == 1)
+					new->bcnt = old->bcnt + next->cnt_surplus.bcnt;
+				else if (cc->change / 3 == 2)
+					new->bcnt = old->bcnt - next->cnt_surplus.bcnt;
+				else
+					new->bcnt = next->cnt.bcnt;
+			} else
+				*new = next->cnt;
+			next->cnt = *new;
+			next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+			if (cc->type == CNT_ADD)
+				new++;
+			else {
+				old++;
+				new++;
+			}
+			next = next->next;
+		}
+		cc = cc->next;
+	}
+
+	free(u_repl->counters);
+	u_repl->counters = newcounters;
+	u_repl->num_counters = u_repl->nentries;
+	/* Reset the counterchanges to CNT_NORM and delete the unused cc */
+	i = 0;
+	cc = u_repl->cc->next;
+	while (cc != u_repl->cc) {
+		if (cc->type == CNT_DEL) {
+			cc->prev->next = cc->next;
+			cc->next->prev = cc->prev;
+			cc2 = cc->next;
+			free(cc);
+			cc = cc2;
+		} else {
+			cc->type = CNT_NORM;
+			cc->change = 0;
+			i++;
+			cc = cc->next;
+		}
+	}
+	if (i != u_repl->nentries)
+		ebt_print_bug("i != u_repl->nentries");
+	if (u_repl->filename != NULL) {
+		store_counters_in_file(u_repl->filename, u_repl);
+		return;
+	}
+	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
+	   sizeof(struct ebt_replace);
+	/* Now put the stuff in the kernel's struct ebt_replace */
+	repl.counters = sparc_cast u_repl->counters;
+	repl.num_counters = u_repl->num_counters;
+	memcpy(repl.name, u_repl->name, sizeof(repl.name));
+
+	if (get_sockfd())
+		return;
+	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+		ebt_print_bug("Couldn't update kernel counters");
+}
+
+static int
+ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
+{
+	struct ebt_u_match_list *new;
+	int ret = 0;
+
+	new = (struct ebt_u_match_list *)
+	   malloc(sizeof(struct ebt_u_match_list));
+	if (!new)
+		ebt_print_memory();
+	new->m = (struct ebt_entry_match *)
+	   malloc(m->match_size + sizeof(struct ebt_entry_match));
+	if (!new->m)
+		ebt_print_memory();
+	memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (ebt_find_match(new->m->u.name) == NULL) {
+		ebt_print_error("Kernel match %s unsupported by userspace tool",
+				new->m->u.name);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int
+ebt_translate_watcher(struct ebt_entry_watcher *w,
+   struct ebt_u_watcher_list ***l)
+{
+	struct ebt_u_watcher_list *new;
+	int ret = 0;
+
+	new = (struct ebt_u_watcher_list *)
+	   malloc(sizeof(struct ebt_u_watcher_list));
+	if (!new)
+		ebt_print_memory();
+	new->w = (struct ebt_entry_watcher *)
+	   malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
+	if (!new->w)
+		ebt_print_memory();
+	memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (ebt_find_watcher(new->w->u.name) == NULL) {
+		ebt_print_error("Kernel watcher %s unsupported by userspace "
+				"tool", new->w->u.name);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int
+ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt,
+   int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl,
+   unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc)
+{
+	/* An entry */
+	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+		struct ebt_u_entry *new;
+		struct ebt_u_match_list **m_l;
+		struct ebt_u_watcher_list **w_l;
+		struct ebt_entry_target *t;
+
+		new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new)
+			ebt_print_memory();
+		new->bitmask = e->bitmask;
+		/*
+		 * Plain userspace code doesn't know about
+		 * EBT_ENTRY_OR_ENTRIES
+		 */
+		new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
+		new->invflags = e->invflags;
+		new->ethproto = e->ethproto;
+		strcpy(new->in, e->in);
+		strcpy(new->out, e->out);
+		strcpy(new->logical_in, e->logical_in);
+		strcpy(new->logical_out, e->logical_out);
+		memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
+		memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
+		memcpy(new->destmac, e->destmac, sizeof(new->destmac));
+		memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
+		if (*totalcnt >= u_repl->nentries)
+			ebt_print_bug("*totalcnt >= u_repl->nentries");
+		new->cnt = u_repl->counters[*totalcnt];
+		new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0;
+		new->cc = *cc;
+		*cc = (*cc)->next;
+		new->m_list = NULL;
+		new->w_list = NULL;
+		new->next = (*u_e)->next;
+		new->next->prev = new;
+		(*u_e)->next = new;
+		new->prev = *u_e;
+		*u_e = new;
+		m_l = &new->m_list;
+		EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
+		w_l = &new->w_list;
+		EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
+
+		t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+		new->t = (struct ebt_entry_target *)
+		   malloc(t->target_size + sizeof(struct ebt_entry_target));
+		if (!new->t)
+			ebt_print_memory();
+		if (ebt_find_target(t->u.name) == NULL) {
+			ebt_print_error("Kernel target %s unsupported by "
+					"userspace tool", t->u.name);
+			return -1;
+		}
+		memcpy(new->t, t, t->target_size +
+		   sizeof(struct ebt_entry_target));
+		/* Deal with jumps to udc */
+		if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
+			char *tmp = base;
+			int verdict = ((struct ebt_standard_target *)t)->verdict;
+			int i;
+
+			if (verdict >= 0) {
+				tmp += verdict;
+				for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++)
+					if (u_repl->chains[i]->kernel_start == tmp)
+						break;
+				if (i == u_repl->num_chains)
+					ebt_print_bug("Can't find udc for jump");
+				((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS;
+			}
+		}
+
+		(*cnt)++;
+		(*totalcnt)++;
+		return 0;
+	} else { /* A new chain */
+		int i;
+		struct ebt_entries *entries = (struct ebt_entries *)e;
+
+		if (*n != *cnt)
+			ebt_print_bug("Nr of entries in the chain is wrong");
+		*n = entries->nentries;
+		*cnt = 0;
+		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+			if (valid_hooks & (1 << i))
+				break;
+		*hook = i;
+		*u_e = u_repl->chains[*hook]->entries;
+		return 0;
+	}
+}
+
+/* Initialize all chain headers */
+static int
+ebt_translate_chains(struct ebt_entry *e, int *hook,
+   struct ebt_u_replace *u_repl, unsigned int valid_hooks)
+{
+	int i;
+	struct ebt_entries *entries = (struct ebt_entries *)e;
+	struct ebt_u_entries *new;
+
+	if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+			if (valid_hooks & (1 << i))
+				break;
+		new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
+		if (!new)
+			ebt_print_memory();
+		if (i == u_repl->max_chains)
+			ebt_double_chains(u_repl);
+		u_repl->chains[i] = new;
+		if (i >= NF_BR_NUMHOOKS)
+			new->kernel_start = (char *)e;
+		*hook = i;
+		new->nentries = entries->nentries;
+		new->policy = entries->policy;
+		new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new->entries)
+			ebt_print_memory();
+		new->entries->next = new->entries->prev = new->entries;
+		new->counter_offset = entries->counter_offset;
+		strcpy(new->name, entries->name);
+	}
+	return 0;
+}
+
+static int retrieve_from_file(char *filename, struct ebt_replace *repl,
+   char command)
+{
+	FILE *file;
+	char *hlp = NULL, *entries;
+	struct ebt_counter *counters;
+	int size, ret = 0;
+
+	if (!(file = fopen(filename, "r+b"))) {
+		ebt_print_error("Could not open file %s", filename);
+		return -1;
+	}
+	/* Make sure table name is right if command isn't -L or --atomic-commit */
+	if (command != 'L' && command != 8) {
+		hlp = (char *)malloc(strlen(repl->name) + 1);
+		if (!hlp)
+			ebt_print_memory();
+		strcpy(hlp, repl->name);
+	}
+	if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
+	   != sizeof(struct ebt_replace)) {
+		ebt_print_error("File %s is corrupt", filename);
+		ret = -1;
+		goto close_file;
+	}
+	if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) {
+		ebt_print_error("File %s contains wrong table name or is "
+				"corrupt", filename);
+		ret = -1;
+		goto close_file;
+	} else if (!ebt_find_table(repl->name)) {
+		ebt_print_error("File %s contains invalid table name",
+				filename);
+		ret = -1;
+		goto close_file;
+	}
+
+	size = sizeof(struct ebt_replace) +
+	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+	fseek(file, 0, SEEK_END);
+	if (size != ftell(file)) {
+		ebt_print_error("File %s has wrong size", filename);
+		ret = -1;
+		goto close_file;
+	}
+	entries = (char *)malloc(repl->entries_size);
+	if (!entries)
+		ebt_print_memory();
+	repl->entries = sparc_cast entries;
+	if (repl->nentries) {
+		counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter));
+		repl->counters = sparc_cast counters;
+		if (!repl->counters)
+			ebt_print_memory();
+	} else
+		repl->counters = sparc_cast NULL;
+	/* Copy entries and counters */
+	if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
+	   fread((char *)repl->entries, sizeof(char), repl->entries_size, file)
+	   != repl->entries_size ||
+	   fseek(file, sizeof(struct ebt_replace) + repl->entries_size,
+		 SEEK_SET)
+	   || (repl->counters && fread((char *)repl->counters, sizeof(char),
+	   repl->nentries * sizeof(struct ebt_counter), file)
+	   != repl->nentries * sizeof(struct ebt_counter))) {
+		ebt_print_error("File %s is corrupt", filename);
+		free(entries);
+		repl->entries = NULL;
+		ret = -1;
+	}
+close_file:
+	fclose(file);
+	free(hlp);
+	return ret;
+}
+
+static int retrieve_from_kernel(struct ebt_replace *repl, char command,
+				int init)
+{
+	socklen_t optlen;
+	int optname;
+	char *entries;
+
+	optlen = sizeof(struct ebt_replace);
+	if (get_sockfd())
+		return -1;
+	/* --atomic-init || --init-table */
+	if (init)
+		optname = EBT_SO_GET_INIT_INFO;
+	else
+		optname = EBT_SO_GET_INFO;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		return -1;
+
+	if ( !(entries = (char *)malloc(repl->entries_size)) )
+		ebt_print_memory();
+	repl->entries = sparc_cast entries;
+	if (repl->nentries) {
+		struct ebt_counter *counters;
+
+		if (!(counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter))) )
+			ebt_print_memory();
+		repl->counters = sparc_cast counters;
+	}
+	else
+		repl->counters = sparc_cast NULL;
+
+	/* We want to receive the counters */
+	repl->num_counters = repl->nentries;
+	optlen += repl->entries_size + repl->num_counters *
+	   sizeof(struct ebt_counter);
+	if (init)
+		optname = EBT_SO_GET_INIT_ENTRIES;
+	else
+		optname = EBT_SO_GET_ENTRIES;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		ebt_print_bug("Hmm, what is wrong??? bug#1");
+
+	return 0;
+}
+
+int ebt_get_table(struct ebt_u_replace *u_repl, int init)
+{
+	int i, j, k, hook;
+	struct ebt_replace repl;
+	struct ebt_u_entry *u_e = NULL;
+	struct ebt_cntchanges *new_cc = NULL, *cc;
+
+	strcpy(repl.name, u_repl->name);
+	if (u_repl->filename != NULL) {
+		if (init)
+			ebt_print_bug("Getting initial table data from a file is impossible");
+		if (retrieve_from_file(u_repl->filename, &repl, u_repl->command))
+			return -1;
+		/* -L with a wrong table name should be dealt with silently */
+		strcpy(u_repl->name, repl.name);
+	} else if (retrieve_from_kernel(&repl, u_repl->command, init))
+		return -1;
+
+	/* Translate the struct ebt_replace to a struct ebt_u_replace */
+	u_repl->valid_hooks = repl.valid_hooks;
+	u_repl->nentries = repl.nentries;
+	u_repl->num_counters = repl.num_counters;
+	u_repl->counters = repl.counters;
+	u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+	if (!u_repl->cc)
+		ebt_print_memory();
+	u_repl->cc->next = u_repl->cc->prev = u_repl->cc;
+	cc = u_repl->cc;
+	for (i = 0; i < repl.nentries; i++) {
+		new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+		if (!new_cc)
+			ebt_print_memory();
+		new_cc->type = CNT_NORM;
+		new_cc->change = 0;
+		new_cc->prev = cc;
+		cc->next = new_cc;
+		cc = new_cc;
+	}
+	if (repl.nentries) {
+		new_cc->next = u_repl->cc;
+		u_repl->cc->prev = new_cc;
+	}
+	u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *));
+	u_repl->max_chains = EBT_ORI_MAX_CHAINS;
+	hook = -1;
+	/* FIXME: Clean up when an error is encountered */
+	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
+	   &hook, u_repl, u_repl->valid_hooks);
+	if (hook >= NF_BR_NUMHOOKS)
+		u_repl->num_chains = hook + 1;
+	else
+		u_repl->num_chains = NF_BR_NUMHOOKS;
+	i = 0; /* Holds the expected nr. of entries for the chain */
+	j = 0; /* Holds the up to now counted entries for the chain */
+	k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */
+	cc = u_repl->cc->next;
+	hook = -1;
+	EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size,
+	   ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl,
+	   u_repl->valid_hooks, (char *)repl.entries, &cc);
+	if (k != u_repl->nentries)
+		ebt_print_bug("Wrong total nentries");
+	free(repl.entries);
+	return 0;
+}
diff --git a/userspace/ebtables2/ebtables-config b/userspace/ebtables2/ebtables-config
new file mode 100644
index 0000000..3a89902
--- /dev/null
+++ b/userspace/ebtables2/ebtables-config
@@ -0,0 +1,37 @@
+# Save (and possibly restore) in text format.
+#   Value: yes|no,  default: yes
+# Save the firewall rules in text format to __SYSCONFIG__/ebtables
+# If EBTABLES_BINARY_FORMAT="no" then restoring the firewall rules
+# is done using this text format.
+EBTABLES_TEXT_FORMAT="yes"
+
+# Save (and restore) in binary format.
+#   Value: yes|no,  default: yes
+# Save (and restore) the firewall rules in binary format to (and from)
+# __SYSCONFIG__/ebtables.<chain>. Enabling this option will make
+# firewall initialisation a lot faster.
+EBTABLES_BINARY_FORMAT="yes"
+
+# Unload modules on restart and stop
+#   Value: yes|no,  default: yes
+# This option has to be 'yes' to get to a sane state for a firewall
+# restart or stop. Only set to 'no' if there are problems unloading netfilter
+# modules.
+EBTABLES_MODULES_UNLOAD="yes"
+
+# Save current firewall rules on stop.
+#   Value: yes|no,  default: no
+# Saves all firewall rules if firewall gets stopped
+# (e.g. on system shutdown).
+EBTABLES_SAVE_ON_STOP="no"
+
+# Save current firewall rules on restart.
+#   Value: yes|no,  default: no
+# Saves all firewall rules if firewall gets restarted.
+EBTABLES_SAVE_ON_RESTART="no"
+
+# Save (and restore) rule counters.
+#   Value: yes|no,  default: no
+# Save rule counters when saving a kernel table to a file. If the
+# rule counters were saved, they will be restored when restoring the table.
+EBTABLES_SAVE_COUNTER="no"
diff --git a/userspace/ebtables2/ebtables-restore b/userspace/ebtables2/ebtables-restore
new file mode 100644
index 0000000..7c2ea88
--- /dev/null
+++ b/userspace/ebtables2/ebtables-restore
@@ -0,0 +1,118 @@
+#!/usr/bin/perl -w
+#
+#
+# A script that imports text ebtables rules. Similar to iptables-restore.
+# It can be used to restore configuration from /etc/sysconfig/ebtables.
+#
+
+use strict;
+my $ebtables = "__EXEC_PATH__/ebtables";
+my $table = "";
+my $rc;
+my $child;
+my $line;
+
+# ==============================
+# Check table
+# Creates user chains.
+# ==============================
+sub check_chain {
+    if ($table eq "filter") {
+        if ($_[1] eq "INPUT") { return; }
+        if ($_[1] eq "FORWARD") { return; }
+        if ($_[1] eq "OUTPUT") { return; }
+    }
+    if ($table eq "nat") {
+        if ($_[1] eq "PREROUTING") { return; }
+        if ($_[1] eq "POSTROUTING") { return; }
+        if ($_[1] eq "OUTPUT") { return; }
+    }
+    if ($table eq "broute") {
+        if ($_[1] eq "BROUTING") { return; }
+    }
+    $rc = `$ebtables -t $_[0] -N $_[1]`;
+    unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+}
+# ==============================
+
+if (-x "__EXEC_PATH__/ebtablesd" && -x "__EXEC_PATH__/ebtablesu") {
+    `killall ebtablesd 2>/dev/null`;
+    $child = fork();
+    if ($child == 0) {
+        $rc = `__EXEC_PATH__/ebtablesd`;
+	if (!($rc eq "")) {
+            exit -1;
+        }
+        exit 0;
+    }
+    $ebtables = "__EXEC_PATH__/ebtablesu";
+    while (!(-e "__PIPE__")) {
+        if ((kill 0) < $child) {
+            exit -1;
+        }
+    }
+} else {
+    unless (-x $ebtables) { print "ERROR: $ebtables isn't executable\n"; exit -1; };
+}
+
+$line = 0;
+while(<>) {
+    $line++;
+    if(m/^#/) { next; };
+    if(m/^$/) { next; };
+    if ($ebtables eq "__EXEC_PATH__/ebtablesu") {
+        if ((kill 0) < $child) {
+            exit -1;
+        }
+    }
+    if(m/^\*(.*)/) {
+        if (!($table eq "")) {
+            if (!defined($ENV{'EBTABLES_SAVE_COUNTER'}) || !($ENV{'EBTABLES_SAVE_COUNTER'} eq "yes")) {
+                $rc = `$ebtables -t $table -Z`;
+                unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+            }
+            if ($ebtables eq "__EXEC_PATH__/ebtablesu") {
+                $rc = `$ebtables commit $table`;
+                $rc = `$ebtables free $table`;
+                unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+           }
+        }
+        $table = $1;
+        if ($ebtables eq "__EXEC_PATH__/ebtablesu") {
+            $rc = `$ebtables open $table`;
+            unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+            $rc = `$ebtables -F`;
+            unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+        } else {
+            $rc = `$ebtables -t filter --init-table`;
+            unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+        }
+        next;
+    }
+    if(m/^\:(.*?)\s(.*)/) {
+        &check_chain($table,$1);
+        $rc = `$ebtables -t $table -P $1 $2`;
+        unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1};
+        next;
+    }
+    $rc = `$ebtables -t $table $_`;
+    unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1};
+}
+
+if (!($table eq "")) {
+    if (!defined($ENV{'EBTABLES_SAVE_COUNTER'}) || !($ENV{'EBTABLES_SAVE_COUNTER'} eq "yes")) {
+        $rc = `$ebtables -t $table -Z`;
+        unless($? == 0) {print "ERROR: '-t $table -Z' failed\n"; exit -1};
+    }
+    if ($ebtables eq "__EXEC_PATH__/ebtablesu") {
+        $rc = `$ebtables commit $table`;
+        unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+    }
+}
+
+if ($ebtables eq "__EXEC_PATH__/ebtablesu") {
+    $rc = `$ebtables quit`;
+    unless($? == 0) {print "ERROR: $rc\n"; exit -1};
+    waitpid($child,0);
+    exit 0;
+}
diff --git a/userspace/ebtables2/ebtables-restore.c b/userspace/ebtables2/ebtables-restore.c
new file mode 100644
index 0000000..ea02960
--- /dev/null
+++ b/userspace/ebtables2/ebtables-restore.c
@@ -0,0 +1,135 @@
+/*
+ * ebtables-restore.c, October 2005
+ *
+ * Author: Bart De Schuymer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "include/ebtables_u.h"
+
+static struct ebt_u_replace replace[3];
+void ebt_early_init_once();
+
+#define OPT_KERNELDATA  0x800 /* Also defined in ebtables.c */
+
+static void copy_table_names()
+{
+	strcpy(replace[0].name, "filter");
+	strcpy(replace[1].name, "nat");
+	strcpy(replace[2].name, "broute");
+}
+
+#define ebtrest_print_error(format, args...) do {fprintf(stderr, "ebtables-restore: "\
+                                             "line %d: "format".\n", line, ##args); exit(-1);} while (0)
+int main(int argc_, char *argv_[])
+{
+	char *argv[EBTD_ARGC_MAX], cmdline[EBTD_CMDLINE_MAXLN];
+	int i, offset, quotemode = 0, argc, table_nr = -1, line = 0, whitespace;
+	char ebtables_str[] = "ebtables";
+
+	if (argc_ != 1)
+		ebtrest_print_error("options are not supported");
+	ebt_silent = 0;
+	copy_table_names();
+	ebt_early_init_once();
+	argv[0] = ebtables_str;
+
+	while (fgets(cmdline, EBTD_CMDLINE_MAXLN, stdin)) {
+		line++;
+		if (*cmdline == '#' || *cmdline == '\n')
+			continue;
+		*strchr(cmdline, '\n') = '\0';
+		if (*cmdline == '*') {
+			if (table_nr != -1) {
+				ebt_deliver_table(&replace[table_nr]);
+				ebt_deliver_counters(&replace[table_nr]);
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, cmdline+1))
+					break;
+			if (i == 3)
+				ebtrest_print_error("table '%s' was not recognized", cmdline+1);
+			table_nr = i;
+			replace[table_nr].command = 11;
+			ebt_get_kernel_table(&replace[table_nr], 1);
+			replace[table_nr].command = 0;
+			replace[table_nr].flags = OPT_KERNELDATA; /* Prevent do_command from initialising replace */
+			continue;
+		} else if (table_nr == -1)
+			ebtrest_print_error("no table specified");
+		if (*cmdline == ':') {
+			int policy, chain_nr;
+			char *ch;
+
+			if (!(ch = strchr(cmdline, ' ')))
+				ebtrest_print_error("no policy specified");
+			*ch = '\0';
+			for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+				if (!strcmp(ch+1, ebt_standard_targets[i])) {
+					policy = -i -1;
+					if (policy == EBT_CONTINUE)
+						i = NUM_STANDARD_TARGETS;
+					break;
+				}
+			if (i == NUM_STANDARD_TARGETS)
+				ebtrest_print_error("invalid policy specified");
+			/* No need to check chain name for consistency, since
+			 * we're supposed to be reading an automatically generated
+			 * file. */
+			if ((chain_nr = ebt_get_chainnr(&replace[table_nr], cmdline+1)) == -1)
+				ebt_new_chain(&replace[table_nr], cmdline+1, policy);
+			else
+				replace[table_nr].chains[chain_nr]->policy = policy;
+			continue;
+		}
+		argv[1] = cmdline;
+		offset = whitespace = 0;
+		argc = 2;
+		while (cmdline[offset] != '\0') {
+			if (cmdline[offset] == '\"') {
+				whitespace = 0;
+				quotemode ^= 1;
+				if (quotemode)
+					argv[argc++] = &cmdline[offset+1];
+				else if (cmdline[offset+1] != ' ' && cmdline[offset+1] != '\0')
+					ebtrest_print_error("syntax error at \"");
+				cmdline[offset] = '\0';
+			} else if (!quotemode && cmdline[offset] == ' ') {
+				whitespace = 1;
+				cmdline[offset] = '\0';
+			} else if (whitespace == 1) {
+				argv[argc++] = &cmdline[offset];
+				whitespace = 0;
+			}
+			offset++;
+		}
+		if (quotemode)
+			ebtrest_print_error("wrong use of '\"'");
+		optind = 0; /* Setting optind = 1 causes serious annoyances */
+		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
+		ebt_reinit_extensions();
+	}
+
+	if (table_nr != -1) {
+		ebt_deliver_table(&replace[table_nr]);
+		ebt_deliver_counters(&replace[table_nr]);
+	}
+	return 0;
+}
diff --git a/userspace/ebtables2/ebtables-save b/userspace/ebtables2/ebtables-save
new file mode 100644
index 0000000..49d733b
--- /dev/null
+++ b/userspace/ebtables2/ebtables-save
@@ -0,0 +1,61 @@
+#!/usr/bin/perl -w
+#
+#
+# A script that generates text output of the ebtables rules.
+# Similar to iptables-save.
+#
+# It can be used to store active configuration to /etc/sysconfig/ebtables
+
+use strict;
+my $table;
+my $ebtables = "__EXEC_PATH__/ebtables";
+my $cnt = "";
+my $version = "1.0";
+my $table_name;
+
+# ========================================================
+# Process filter table
+# ========================================================
+sub process_table {
+    my $chain = "";
+    my $rules = "";
+    my $chains = "";
+    my $line = "";
+
+    foreach $line (split("\n",$_[0])) {
+        if ($line =~ m/Bridge table: (.*)/) {
+            print "*$1\n";
+            next;
+        }
+        if ($line =~ m/Bridge chain: (.*?), entries:.* policy: (.*)/) {
+            $chains = $chains . ":$1 $2\n";
+            $chain = $1;
+            next;
+        }
+        if ($line =~ m/^$/) {
+            next;
+        }
+        if ($cnt eq "--Lc") {
+            $line =~ s/, pcnt = (.*) -- bcnt = (.*)/-c $1 $2/;
+        } else {
+            $line =~ s/ $//;
+        }
+        $rules = $rules . "-A $chain $line\n";
+    }
+
+    print $chains;
+    print $rules;
+    print "\n";
+}
+# ========================================================
+
+unless (-x $ebtables) { exit -1 };
+print "# Generated by ebtables-save v$version on " . `date`;
+if (defined($ENV{'EBTABLES_SAVE_COUNTER'}) && $ENV{'EBTABLES_SAVE_COUNTER'} eq "yes") {
+    $cnt = "--Lc";
+}
+foreach $table_name (split("\n", `grep -E '^ebtable_' /proc/modules | cut -f1 -d' ' | sed s/ebtable_//`)) {
+    $table =`$ebtables -t $table_name -L $cnt`;
+    unless ($? == 0) { print $table; exit -1 };
+    &process_table($table);
+}
diff --git a/userspace/ebtables2/ebtables-standalone.c b/userspace/ebtables2/ebtables-standalone.c
new file mode 100644
index 0000000..d349d39
--- /dev/null
+++ b/userspace/ebtables2/ebtables-standalone.c
@@ -0,0 +1,14 @@
+#include <string.h>
+#include "include/ebtables_u.h"
+
+static struct ebt_u_replace replace;
+void ebt_early_init_once();
+
+int main(int argc, char *argv[])
+{
+	ebt_silent = 0;
+	ebt_early_init_once();
+	strcpy(replace.name, "filter");
+	do_command(argc, argv, EXEC_STYLE_PRG, &replace);
+	return 0;
+}
diff --git a/userspace/ebtables2/ebtables.8 b/userspace/ebtables2/ebtables.8
new file mode 100644
index 0000000..81d1cf6
--- /dev/null
+++ b/userspace/ebtables2/ebtables.8
@@ -0,0 +1,1114 @@
+.TH EBTABLES 8  "$(DATE)"
+.\"
+.\" Man page written by Bart De Schuymer <bdschuym@pandora.be>
+.\" It is based on the iptables man page.
+.\"
+.\" The man page was edited, February 25th 2003, by 
+.\"      Greg Morgan <" dr_kludge_at_users_sourceforge_net >
+.\"
+.\" Iptables page by Herve Eychenne March 2000.
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"     
+.\"
+.SH NAME
+ebtables (v$(VERSION)) \- Ethernet bridge frame table administration
+.SH SYNOPSIS
+.BR "ebtables " [ -t " table ] " - [ ACDI "] chain rule specification [match extensions] [watcher extensions] target"
+.br
+.BR "ebtables " [ -t " table ] " -P " chain " ACCEPT " | " DROP " | " RETURN
+.br
+.BR "ebtables " [ -t " table ] " -F " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -Z " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -L " [" -Z "] [chain] [ [" --Ln "] | [" --Lx "] ] [" --Lc "] [" --Lmac2 ]
+.br
+.BR "ebtables " [ -t " table ] " -N " chain [" "-P ACCEPT " | " DROP " | " RETURN" ]
+.br
+.BR "ebtables " [ -t " table ] " -X " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -E " old-chain-name new-chain-name"
+.br
+.BR "ebtables " [ -t " table ] " --init-table
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-commit
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-init
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-save
+.br
+.SH DESCRIPTION
+.B ebtables
+is an application program used to set up and maintain the
+tables of rules (inside the Linux kernel) that inspect
+Ethernet frames.
+It is analogous to the
+.B iptables
+application, but less complicated, due to the fact that the Ethernet protocol
+is much simpler than the IP protocol.
+.SS CHAINS
+There are three ebtables tables with built-in chains in the
+Linux kernel. These tables are used to divide functionality into
+different sets of rules. Each set of rules is called a chain.
+Each chain is an ordered list of rules that can match Ethernet frames. If a
+rule matches an Ethernet frame, then a processing specification tells
+what to do with that matching frame. The processing specification is
+called a 'target'. However, if the frame does not match the current
+rule in the chain, then the next rule in the chain is examined and so forth.
+The user can create new (user-defined) chains that can be used as the 'target'
+of a rule. User-defined chains are very useful to get better performance
+over the linear traversal of the rules and are also essential for structuring
+the filtering rules into well-organized and maintainable sets of rules.
+.SS TARGETS
+A firewall rule specifies criteria for an Ethernet frame and a frame
+processing specification called a target.  When a frame matches a rule,
+then the next action performed by the kernel is specified by the target.
+The target can be one of these values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+an 'extension' (see below) or a jump to a user-defined chain.
+.PP
+.B ACCEPT
+means to let the frame through.
+.B DROP
+means the frame has to be dropped. In the
+.BR BROUTING " chain however, the " ACCEPT " and " DROP " target have different"
+meanings (see the info provided for the
+.BR -t " option)."
+.B CONTINUE
+means the next rule has to be checked. This can be handy, f.e., to know how many
+frames pass a certain point in the chain, to log those frames or to apply multiple
+targets on a frame.
+.B RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.
+For the extension targets please refer to the
+.B "TARGET EXTENSIONS"
+section of this man page.
+.SS TABLES
+As stated earlier, there are three ebtables tables in the Linux
+kernel.  The table names are
+.BR filter ", " nat " and " broute .
+Of these three tables,
+the filter table is the default table that the command operates on.
+If you are working with the filter table, then you can drop the '-t filter'
+argument to the ebtables command.  However, you will need to provide
+the -t argument for the other two tables.  Moreover, the -t argument must be the
+first argument on the ebtables command line, if used. 
+.TP
+.B "-t, --table"
+.br
+.B filter
+is the default table and contains three built-in chains:
+.B INPUT 
+(for frames destined for the bridge itself, on the level of the MAC destination address), 
+.B OUTPUT 
+(for locally-generated or (b)routed frames) and
+.B FORWARD 
+(for frames being forwarded by the bridge).
+.br
+.br
+.B nat
+is mostly used to change the mac addresses and contains three built-in chains:
+.B PREROUTING 
+(for altering frames as soon as they come in), 
+.B OUTPUT 
+(for altering locally generated or (b)routed frames before they are bridged) and 
+.B POSTROUTING
+(for altering frames as they are about to go out). A small note on the naming
+of chains PREROUTING and POSTROUTING: it would be more accurate to call them
+PREFORWARDING and POSTFORWARDING, but for all those who come from the
+iptables world to ebtables it is easier to have the same names. Note that you
+can change the name
+.BR "" ( -E )
+if you don't like the default.
+.br
+.br
+.B broute
+is used to make a brouter, it has one built-in chain:
+.BR BROUTING .
+The targets
+.BR DROP " and " ACCEPT
+have a special meaning in the broute table (these names are used instead of
+more descriptive names to keep the implementation generic).
+.B DROP
+actually means the frame has to be routed, while
+.B ACCEPT
+means the frame has to be bridged. The
+.B BROUTING
+chain is traversed very early. However, it is only traversed by frames entering on
+a bridge port that is in forwarding state. Normally those frames
+would be bridged, but you can decide otherwise here. The
+.B redirect
+target is very handy here.
+.SH EBTABLES COMMAND LINE ARGUMENTS
+After the initial ebtables '-t table' command line argument, the remaining
+arguments can be divided into several groups.  These groups
+are commands, miscellaneous commands, rule specifications, match extensions,
+watcher extensions and target extensions.
+.SS COMMANDS
+The ebtables command arguments specify the actions to perform on the table
+defined with the -t argument.  If you do not use the -t argument to name
+a table, the commands apply to the default filter table.
+Only one command may be used on the command line at a time, except when
+the commands
+.BR -L " and " -Z
+are combined, the commands
+.BR -N " and " -P
+are combined, or when
+.B --atomic-file
+is used.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to delete (directly after
+.BR -D ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). When \fIend_nr\fP is omitted, all rules starting
+from \fIstart_nr\fP are deleted. Using negative numbers is allowed, for more
+details about using negative numbers, see the
+.B -I
+command. The second usage is by
+specifying the complete rule as it would have been specified when it was added. Only
+the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, is deleted.
+.TP
+.B "-C, --change-counters"
+Change the counters of the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to do the changes on (directly after
+.BR -C ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). The details are the same as for the
+.BR -D " command. The second usage is by"
+specifying the complete rule as it would have been specified when it was added. Only
+the counters of the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, are changed.
+In the first usage, the counters are specified directly after the interval specification,
+in the second usage directly after
+.BR -C .
+First the packet counter is specified, then the byte counter. If the specified counters start
+with a '+', the counter values are added to the respective current counter values.
+If the specified counters start with a '-', the counter values are decreased from the respective
+current counter values. No bounds checking is done. If the counters don't start with '+' or '-',
+the current counters are changed to the specified counters.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number. If the
+rule number is not specified, the rule is added at the head of the chain.
+If the current number of rules equals
+.IR N ,
+then the specified number can be
+between
+.IR -N " and " N+1 .
+For a positive number
+.IR i ,
+it holds that
+.IR i " and " i-N-1
+specify the same place in the chain where the rule should be inserted. The rule number
+0 specifies the place past the last rule in the chain and using this number is therefore
+equivalent to using the
+.BR -A " command."
+Rule numbers structly smaller than 0 can be useful when more than one rule needs to be inserted
+in a chain.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy can be
+.BR ACCEPT ", " DROP " or " RETURN .
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, then every chain will be
+flushed. Flushing a chain does not change the policy of the
+chain, however.
+.TP
+.B "-Z, --zero"
+Set the counters of the selected chain to zero. If no chain is selected, all the counters
+are set to zero. The
+.B "-Z"
+command can be used in conjunction with the 
+.B "-L"
+command.
+When both the
+.B "-Z"
+and
+.B "-L"
+commands are used together in this way, the rule counters are printed on the screen
+before they are set to zero.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed.
+.br
+The following options change the output of the
+.B "-L"
+command.
+.br
+.B "--Ln"
+.br
+Places the rule number in front of every rule. This option is incompatible with the
+.BR --Lx " option."
+.br
+.B "--Lc"
+.br
+Shows the counters at the end of each rule displayed by the
+.B "-L"
+command. Both a frame counter (pcnt) and a byte counter (bcnt) are displayed.
+The frame counter shows how many frames have matched the specific rule, the byte
+counter shows the sum of the frame sizes of these matching frames. Using this option
+.BR "" "in combination with the " --Lx " option causes the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lx"
+.br
+Changes the output so that it produces a set of ebtables commands that construct
+the contents of the chain, when specified.
+If no chain is specified, ebtables commands to construct the contents of the
+table are given, including commands for creating the user-defined chains (if any).
+You can use this set of commands in an ebtables boot or reload
+script.  For example the output could be used at system startup.
+The 
+.B "--Lx"
+option is incompatible with the
+.B "--Ln"
+listing option. Using the
+.BR --Lx " option together with the " --Lc " option will cause the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lmac2"
+.br
+Shows all MAC addresses with the same length, adding leading zeroes
+if necessary. The default representation omits leading zeroes in the addresses.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain with the given name. The number of
+user-defined chains is limited only by the number of possible chain names.
+A user-defined chain name has a maximum
+length of 31 characters. The standard policy of the user-defined chain is
+ACCEPT. The policy of the new chain can be initialized to a different standard
+target by using the
+.B -P
+command together with the
+.B -N
+command. In this case, the chain name does not have to be specified for the
+.B -P
+command.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no remaining references (jumps)
+to the specified chain, otherwise ebtables will refuse to delete it. If no chain is
+specified, all user-defined chains that aren't referenced will be removed.
+.TP
+.B "-E, --rename-chain"
+Rename the specified chain to a new name.  Besides renaming a user-defined
+chain, you can rename a standard chain to a name that suits your
+taste. For example, if you like PREFORWARDING more than PREROUTING,
+then you can use the -E command to rename the PREROUTING chain. If you do
+rename one of the standard ebtables chain names, please be sure to mention
+this fact should you post a question on the ebtables mailing lists.
+It would be wise to use the standard name in your post. Renaming a standard
+ebtables chain in this fashion has no effect on the structure or functioning
+of the ebtables kernel table.
+.TP
+.B "--init-table"
+Replace the current table data by the initial table data.
+.TP
+.B "--atomic-init"
+Copy the kernel's initial data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-save"
+Copy the kernel's current data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-commit"
+Replace the kernel table data with the data contained in the specified
+file. This is a useful command that allows you to load all your rules of a
+certain table into the kernel at once, saving the kernel a lot of precious
+time and allowing atomic updates of the tables. The file which contains
+the table data is constructed by using either the
+.B "--atomic-init"
+or the
+.B "--atomic-save"
+command to generate a starting file. After that, using the
+.B "--atomic-file"
+command when constructing rules or setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable"
+allows you to extend the file and build the complete table before
+committing it to the kernel. This command can be very useful in boot scripts
+to populate the ebtables tables in a fast way.
+.SS MISCELLANOUS COMMANDS
+.TP
+.B "-V, --version"
+Show the version of the ebtables userspace program.
+.TP
+.BR "-h, --help " "[\fIlist of module names\fP]"
+Give a brief description of the command syntax. Here you can also specify
+names of extensions and ebtables will try to write help about those
+extensions. E.g.
+.IR "ebtables -h snat log ip arp" .
+Specify
+.I list_extensions
+to list all extensions supported by the userspace
+utility.
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+a target extension (see
+.BR "TARGET EXTENSIONS" ")"
+or a user-defined chain name.
+.TP
+.B --atomic-file "\fIfile\fP"
+Let the command operate on the specified
+.IR file .
+The data of the table to
+operate on will be extracted from the file and the result of the operation
+will be saved back into the file. If specified, this option should come
+before the command specification. An alternative that should be preferred,
+is setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B -M, --modprobe "\fIprogram\fP"
+When talking to the kernel, use this
+.I program
+to try to automatically load missing kernel modules.
+.TP
+.B --concurrent
+Use a file lock to support concurrent scripts updating the ebtables kernel tables.
+
+.SS
+RULE SPECIFICATIONS
+The following command line arguments make up a rule specification (as used 
+in the add and delete commands). A "!" option before the specification 
+inverts the test for that specification. Apart from these standard rule 
+specifications there are some other command line arguments of interest.
+See both the 
+.BR "MATCH EXTENSIONS" 
+and the
+.BR "WATCHER EXTENSIONS" 
+below.
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol that was responsible for creating the frame. This can be a
+hexadecimal number, above 
+.IR 0x0600 ,
+a name (e.g.
+.I ARP
+) or
+.BR LENGTH .
+The protocol field of the Ethernet frame can be used to denote the
+length of the header (802.2/802.3 networks). When the value of that field is
+below or equals
+.IR 0x0600 ,
+the value equals the size of the header and shouldn't be used as a
+protocol number. Instead, all frames where the protocol field is used as
+the length field are assumed to be of the same 'protocol'. The protocol
+name used in ebtables for these frames is
+.BR LENGTH .
+.br
+The file
+.B /etc/ethertypes
+can be used to show readable
+characters instead of hexadecimal numbers for the protocols. For example,
+.I 0x0800
+will be represented by 
+.IR IPV4 .
+The use of this file is not case sensitive. 
+See that file for more information. The flag 
+.B --proto
+is an alias for this option.
+.TP 
+.BR "-i, --in-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --in-if
+is an alias for this option.
+.TP
+.BR "--logical-in " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is going to be sent (this option is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --out-if
+is an alias for this option.
+.TP
+.BR "--logical-out " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is going to be sent (this option
+is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source MAC address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons. Alternatively one can specify Unicast,
+Multicast, Broadcast or BGA (Bridge Group Address):
+.br
+.IR "Unicast" "=00:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Multicast" "=01:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Broadcast" "=ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff or"
+.IR "BGA" "=01:80:c2:00:00:00/ff:ff:ff:ff:ff:ff."
+Note that a broadcast
+address will also match the multicast specification. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination MAC address. See
+.B -s
+(above) for more details on MAC addresses. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BR "-c, --set-counter " "\fIpcnt bcnt\fP"
+If used with
+.BR -A " or " -I ", then the packet and byte counters of the new rule will be set to
+.IR pcnt ", resp. " bcnt ".
+If used with the
+.BR -C " or " -D " commands, only rules with a packet and byte count equal to"
+.IR pcnt ", resp. " bcnt " will match."
+
+.SS MATCH EXTENSIONS
+Ebtables extensions are dynamically loaded into the userspace tool,
+there is therefore no need to explicitly load them with a
+-m option like is done in iptables.
+These extensions deal with functionality supported by kernel modules supplemental to
+the core ebtables code.
+.SS 802_3
+Specify 802.3 DSAP/SSAP fields or SNAP type.  The protocol must be specified as
+.IR "LENGTH " "(see the option " " -p " above).
+.TP
+.BR "--802_3-sap " "[!] \fIsap\fP"
+DSAP and SSAP are two one byte 802.3 fields.  The bytes are always
+equal, so only one byte (hexadecimal) is needed as an argument.
+.TP
+.BR "--802_3-type " "[!] \fItype\fP"
+If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must
+be consulted to determine the payload protocol.  This is a two byte
+(hexadecimal) argument.  Only 802.3 frames with DSAP/SSAP 0xaa are
+checked for type.
+.SS among
+Match a MAC address or MAC/IP address pair versus a list of MAC addresses
+and MAC/IP address pairs.
+A list entry has the following format:
+.IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
+list entries are separated by a comma, specifying an IP address corresponding to
+the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
+but different IP address (and vice versa) can be specified. If the MAC address doesn't
+match any entry from the list, the frame doesn't match the rule (unless "!" was used).
+.TP
+.BR "--among-dst " "[!] \fIlist\fP"
+Compare the MAC destination to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP destination address pairs from the
+list is possible.
+.TP
+.BR "--among-src " "[!] \fIlist\fP"
+Compare the MAC source to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP source address pairs from the list
+is possible.
+.TP
+.BR "--among-dst-file " "[!] \fIfile\fP"
+Same as
+.BR --among-dst " but the list is read in from the specified file."
+.TP
+.BR "--among-src-file " "[!] \fIfile\fP"
+Same as
+.BR --among-src " but the list is read in from the specified file."
+.SS arp
+Specify (R)ARP fields. The protocol must be specified as
+.IR ARP " or " RARP .
+.TP
+.BR "--arp-opcode " "[!] \fIopcode\fP"
+The (R)ARP opcode (decimal or a string, for more details see
+.BR "ebtables -h arp" ).
+.TP
+.BR "--arp-htype " "[!] \fIhardware type\fP"
+The hardware type, this can be a decimal or the string
+.I Ethernet
+(which sets
+.I type
+to 1). Most (R)ARP packets have Eternet as hardware type.
+.TP
+.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+The protocol type for which the (r)arp is used (hexadecimal or the string
+.IR IPv4 ,
+denoting 0x0800).
+Most (R)ARP packets have protocol type IPv4.
+.TP
+.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP source address specification.
+.TP
+.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP destination address specification.
+.TP
+.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC source address specification.
+.TP
+.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC destination address specification.
+.TP
+.BR "" "[!]" " --arp-gratuitous"
+Checks for ARP gratuitous packets: checks equality of IPv4 source
+address and IPv4 destination address inside the ARP header.
+.SS ip
+Specify IPv4 fields. The protocol must be specified as
+.IR IPv4 .
+.TP
+.BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IP address.
+The flag
+.B --ip-src
+is an alias for this option.
+.TP
+.BR "--ip-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IP address.
+The flag
+.B --ip-dst
+is an alias for this option.
+.TP
+.BR "--ip-tos " "[!] \fItos\fP"
+The IP type of service, in hexadecimal numbers.
+.BR IPv4 .
+.TP
+.BR "--ip-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip-proto
+is an alias for this option.
+.TP
+.BR "--ip-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IP protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-sport
+is an alias for this option.
+.TP
+.BR "--ip-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for ip protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-dport
+is an alias for this option.
+.SS ip6
+Specify IPv6 fields. The protocol must be specified as
+.IR IPv6 .
+.TP
+.BR "--ip6-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IPv6 address.
+The flag
+.B --ip6-src
+is an alias for this option.
+.TP
+.BR "--ip6-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IPv6 address.
+The flag
+.B --ip6-dst
+is an alias for this option.
+.TP
+.BR "--ip6-tclass " "[!] \fItclass\fP"
+The IPv6 traffic class, in hexadecimal numbers.
+.TP
+.BR "--ip6-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip6-proto
+is an alias for this option.
+.TP
+.BR "--ip6-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-sport
+is an alias for this option.
+.TP
+.BR "--ip6-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-dport
+is an alias for this option.
+.TP
+.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}"
+Specify ipv6\-icmp type and code to match.
+Ranges for both type and code are supported. Type and code are
+separated by a slash. Valid numbers for type and range are 0 to 255.
+To match a single type including all valid codes, symbolic names can
+be used instead of numbers. The list of known type names is shown by the command
+.nf
+  ebtables \-\-help ip6
+.fi
+This option is only valid for \-\-ip6-prococol ipv6-icmp.
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached.
+It can be used with the
+.B --log
+watcher to give limited logging, for example. Its use is the same
+as the limit match of iptables.
+.TP
+.BR "--limit " "[\fIvalue\fP]"
+Maximum average matching rate: specified as a number, with an optional
+.IR /second ", " /minute ", " /hour ", or " /day " suffix; the default is " 3/hour .
+.TP
+.BR "--limit-burst " "[\fInumber\fP]"
+Maximum initial number of packets to match: this number gets recharged by
+one every time the limit specified above is not reached, up to this
+number; the default is
+.IR 5 .
+.SS mark_m
+.TP
+.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
+Matches frames with the given unsigned mark value. If a
+.IR value " and " mask " are specified, the logical AND of the mark value of the frame and"
+the user-specified
+.IR mask " is taken before comparing it with the"
+user-specified mark
+.IR value ". When only a mark "
+.IR value " is specified, the packet"
+only matches when the mark value of the frame equals the user-specified
+mark
+.IR value .
+If only a
+.IR mask " is specified, the logical"
+AND of the mark value of the frame and the user-specified
+.IR mask " is taken and the frame matches when the result of this logical AND is"
+non-zero. Only specifying a
+.IR mask " is useful to match multiple mark values."
+.SS pkttype
+.TP
+.BR "--pkttype-type " "[!] \fItype\fP"
+Matches on the Ethernet "class" of the frame, which is determined by the
+generic networking code. Possible values:
+.IR broadcast " (MAC destination is the broadcast address),"
+.IR multicast " (MAC destination is a multicast address),"
+.IR host " (MAC destination is the receiving network device), or "
+.IR otherhost " (none of the above)."
+.SS stp
+Specify stp BPDU (bridge protocol data unit) fields. The destination
+address
+.BR "" ( -d ") must be specified as the bridge group address"
+.IR "" ( BGA ).
+For all options for which a range of values can be specified, it holds that
+if the lower bound is omitted (but the colon is not), then the lowest possible lower bound
+for that option is used, while if the upper bound is omitted (but the colon again is not), the
+highest possible upper bound for that option is used.
+.TP
+.BR "--stp-type " "[!] \fItype\fP"
+The BPDU type (0-255), recognized non-numerical types are
+.IR config ", denoting a configuration BPDU (=0), and"
+.IR tcn ", denothing a topology change notification BPDU (=128)."
+.TP
+.BR "--stp-flags " "[!] \fIflag\fP"
+The BPDU flag (0-255), recognized non-numerical flags are
+.IR topology-change ", denoting the topology change flag (=1), and"
+.IR topology-change-ack ", denoting the topology change acknowledgement flag (=128)."
+.TP
+.BR "--stp-root-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The root priority (0-65535) range.
+.TP
+.BR "--stp-root-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The root mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-root-cost " "[!] [\fIcost\fP][:\fIcost\fP]"
+The root path cost (0-4294967295) range.
+.TP
+.BR "--stp-sender-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The BPDU's sender priority (0-65535) range.
+.TP
+.BR "--stp-sender-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The BPDU's sender mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-port " "[!] [\fIport\fP][:\fIport\fP]"
+The port identifier (0-65535) range.
+.TP
+.BR "--stp-msg-age " "[!] [\fIage\fP][:\fIage\fP]"
+The message age timer (0-65535) range.
+.TP
+.BR "--stp-max-age " "[!] [\fIage\fP][:\fIage\fP]"
+The max age timer (0-65535) range.
+.TP
+.BR "--stp-hello-time " "[!] [\fItime\fP][:\fItime\fP]"
+The hello time timer (0-65535) range.
+.TP
+.BR "--stp-forward-delay " "[!] [\fIdelay\fP][:\fIdelay\fP]"
+The forward delay timer (0-65535) range.
+.SS vlan
+Specify 802.1Q Tag Control Information fields.
+The protocol must be specified as
+.IR 802_1Q " (0x8100)."
+.TP
+.BR "--vlan-id " "[!] \fIid\fP"
+The VLAN identifier field (VID). Decimal number from 0 to 4095.
+.TP
+.BR "--vlan-prio " "[!] \fIprio\fP"
+The user priority field, a decimal number from 0 to 7.
+The VID should be set to 0 ("null VID") or unspecified
+(in the latter case the VID is deliberately set to 0).
+.TP
+.BR "--vlan-encap " "[!] \fItype\fP"
+The encapsulated Ethernet frame type/length.
+Specified as a hexadecimal
+number from 0x0000 to 0xFFFF or as a symbolic name
+from
+.BR /etc/ethertypes .
+
+.SS WATCHER EXTENSIONS
+Watchers only look at frames passing by, they don't modify them nor decide
+to accept the frames or not. These watchers only
+see the frame if the frame matches the rule, and they see it before the
+target is executed.
+.SS log
+The log watcher writes descriptive data about a frame to the syslog.
+.TP
+.B "--log"
+.br
+Log with the default loggin options: log-level=
+.IR info ,
+log-prefix="", no ip logging, no arp logging.
+.TP
+.B --log-level "\fIlevel\fP"
+.br
+Defines the logging level. For the possible values, see
+.BR "ebtables -h log" .
+The default level is 
+.IR info .
+.TP
+.BR --log-prefix " \fItext\fP"
+.br
+Defines the prefix
+.I text
+to be printed at the beginning of the line with the logging information.
+.TP
+.B --log-ip 
+.br
+Will log the ip information when a frame made by the ip protocol matches 
+the rule. The default is no ip information logging.
+.TP
+.B --log-ip6 
+.br
+Will log the ipv6 information when a frame made by the ipv6 protocol matches 
+the rule. The default is no ipv6 information logging.
+.TP
+.B --log-arp
+.br
+Will log the (r)arp information when a frame made by the (r)arp protocols
+matches the rule. The default is no (r)arp information logging.
+.SS nflog
+The nflog watcher passes the packet to the loaded logging backend
+in order to log the packet. This is usually used in combination with
+nfnetlink_log as logging backend, which will multicast the packet
+through a
+.IR netlink
+socket to the specified multicast group. One or more userspace processes
+may subscribe to the group to receive the packets.
+.TP
+.B "--nflog"
+.br
+Log with the default logging options
+.TP
+.B --nflog-group "\fInlgroup\fP"
+.br
+The netlink group (1 - 2^32-1) to which packets are (only applicable for
+nfnetlink_log). The default value is 1.
+.TP
+.B --nflog-prefix "\fIprefix\fP"
+.br
+A prefix string to include in the log message, up to 30 characters
+long, useful for distinguishing messages in the logs.
+.TP
+.B --nflog-range "\fIsize\fP"
+.br
+The number of bytes to be copied to userspace (only applicable for
+nfnetlink_log). nfnetlink_log instances may specify their own
+range, this option overrides it.
+.TP
+.B --nflog-threshold "\fIsize\fP"
+.br
+Number of packets to queue inside the kernel before sending them
+to userspace (only applicable for nfnetlink_log). Higher values
+result in less overhead per packet, but increase delay until the
+packets reach userspace. The default value is 1.
+.SS ulog
+The ulog watcher passes the packet to a userspace
+logging daemon using netlink multicast sockets. This differs
+from the log watcher in the sense that the complete packet is
+sent to userspace instead of a descriptive text and that
+netlink multicast sockets are used instead of the syslog.
+This watcher enables parsing of packets with userspace programs, the
+physical bridge in and out ports are also included in the netlink messages.
+The ulog watcher module accepts 2 parameters when the module is loaded
+into the kernel (e.g. with modprobe):
+.B nlbufsiz
+specifies how big the buffer for each netlink multicast
+group is. If you say
+.IR nlbufsiz=8192 ,
+for example, up to eight kB of packets will
+get accumulated in the kernel until they are sent to userspace. It is
+not possible to allocate more than 128kB. Please also keep in mind that
+this buffer size is allocated for each nlgroup you are using, so the
+total kernel memory usage increases by that factor. The default is 4096.
+.B flushtimeout
+specifies after how many hundredths of a second the queue should be
+flushed, even if it is not full yet. The default is 10 (one tenth of
+a second).
+.TP
+.B "--ulog"
+.br
+Use the default settings: ulog-prefix="", ulog-nlgroup=1,
+ulog-cprange=4096, ulog-qthreshold=1.
+.TP
+.B --ulog-prefix "\fItext\fP"
+.br
+Defines the prefix included with the packets sent to userspace.
+.TP
+.BR --ulog-nlgroup " \fIgroup\fP"
+.br
+Defines which netlink group number to use (a number from 1 to 32).
+Make sure the netlink group numbers used for the iptables ULOG
+target differ from those used for the ebtables ulog watcher.
+The default group number is 1.
+.TP
+.BR --ulog-cprange " \fIrange\fP"
+.br
+Defines the maximum copy range to userspace, for packets matching the
+rule. The default range is 0, which means the maximum copy range is
+given by
+.BR nlbufsiz .
+A maximum copy range larger than
+128*1024 is meaningless as the packets sent to userspace have an upper
+size limit of 128*1024.
+.TP
+.BR --ulog-qthreshold " \fIthreshold\fP"
+.br
+Queue at most
+.I threshold
+number of packets before sending them to
+userspace with a netlink socket. Note that packets can be sent to
+userspace before the queue is full, this happens when the ulog
+kernel timer goes off (the frequency of this timer depends on
+.BR flushtimeout ).
+.SS TARGET EXTENSIONS
+.SS arpreply
+The
+.B arpreply
+target can be used in the
+.BR PREROUTING " chain of the " nat " table."
+If this target sees an ARP request it will automatically reply
+with an ARP reply. The used MAC address for the reply can be specified.
+The protocol must be specified as
+.IR ARP .
+When the ARP message is not an ARP request or when the ARP request isn't
+for an IP address on an Ethernet network, it is ignored by this target
+.BR "" ( CONTINUE ).
+When the ARP request is malformed, it is dropped
+.BR "" ( DROP ).
+.TP
+.BR "--arpreply-mac " "\fIaddress\fP"
+Specifies the MAC address to reply with: the Ethernet source MAC and the
+ARP payload source MAC will be filled in with this address.
+.TP
+.BR "--arpreply-target " "\fItarget\fP"
+Specifies the standard target. After sending the ARP reply, the rule still
+has to give a standard target so ebtables knows what to do with the ARP request.
+The default target
+.BR "" "is " DROP .
+.SS dnat
+The
+.B dnat
+target can only be used in the
+.BR BROUTING " chain of the " broute " table and the "
+.BR PREROUTING " and " OUTPUT " chains of the " nat " table."
+It specifies that the destination MAC address has to be changed.
+.TP
+.BR "--to-destination " "\fIaddress\fP"
+.br
+Change the destination MAC address to the specified
+.IR address .
+The flag
+.B --to-dst
+is an alias for this option.
+.TP
+.BR "--dnat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the dnat, the rule still has to
+give a standard target so ebtables knows what to do with the dnated frame.
+The default target is
+.BR ACCEPT .
+Making it
+.BR CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " only makes"
+sense in the
+.BR BROUTING " chain but using the " redirect " target is more logical there. " RETURN " is also allowed. Note that using " RETURN
+in a base chain is not allowed (for obvious reasons).
+.SS mark
+.BR "" "The " mark " target can be used in every chain of every table. It is possible"
+to use the marking of a frame/packet in both ebtables and iptables,
+if the bridge-nf code is compiled into the kernel. Both put the marking at the
+same place. This allows for a form of communication between ebtables and iptables.
+.TP
+.BR "--mark-set " "\fIvalue\fP"
+.br
+Mark the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-or " "\fIvalue\fP"
+.br
+Or the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-and " "\fIvalue\fP"
+.br
+And the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-xor " "\fIvalue\fP"
+.br
+Xor the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-target " "\fItarget\fP"
+.br
+Specifies the standard target. After marking the frame, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " can let you do other"
+things with the frame in subsequent rules of the chain.
+.SS redirect
+The
+.B redirect
+target will change the MAC target address to that of the bridge device the
+frame arrived on. This target can only be used in the
+.BR BROUTING " chain of the " broute " table and the "
+.BR PREROUTING " chain of the " nat " table."
+In the
+.BR BROUTING " chain, the MAC address of the bridge port is used as destination address,"
+.BR "" "in the " PREROUTING " chain, the MAC address of the bridge is used."
+.TP
+.BR "--redirect-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the MAC redirect, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " in the " BROUTING " chain will let the frames be routed. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.SS snat
+The
+.B snat
+target can only be used in the
+.BR POSTROUTING " chain of the " nat " table."
+It specifies that the source MAC address has to be changed.
+.TP
+.BR "--to-source " "\fIaddress\fP"
+.br
+Changes the source MAC address to the specified
+.IR address ". The flag"
+.B --to-src
+is an alias for this option.
+.TP
+.BR "--snat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the snat, the rule still has 
+to give a standard target so ebtables knows what to do.
+.BR "" "The default target is " ACCEPT ". Making it " CONTINUE " could let you use"
+.BR "" "multiple target extensions on the same frame. Making it " DROP " doesn't"
+.BR "" "make sense, but you could do that too. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.br
+.TP
+.BR "--snat-arp "
+.br
+Also change the hardware source address inside the arp header if the packet is an
+arp message and the hardware address length in the arp header is 6 bytes.
+.br
+.SH FILES
+.I /etc/ethertypes
+.I $(LOCKFILE)
+.SH ENVIRONMENT VARIABLES
+.I EBTABLES_ATOMIC_FILE
+.SH MAILINGLISTS
+.BR "" "See " http://netfilter.org/mailinglists.html
+.SH SEE ALSO
+.BR iptables "(8), " brctl "(8), " ifconfig "(8), " route (8)
+.PP
+.BR "" "See " http://ebtables.sf.net
diff --git a/userspace/ebtables2/ebtables.c b/userspace/ebtables2/ebtables.c
new file mode 100644
index 0000000..62f1ba8
--- /dev/null
+++ b/userspace/ebtables2/ebtables.c
@@ -0,0 +1,1247 @@
+/*
+ * ebtables.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code was stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <signal.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+
+/* Checks whether a command has already been specified */
+#define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->flags & OPT_ZERO)
+
+#define OPT_COMMAND	0x01
+#define OPT_TABLE	0x02
+#define OPT_IN		0x04
+#define OPT_OUT		0x08
+#define OPT_JUMP	0x10
+#define OPT_PROTOCOL	0x20
+#define OPT_SOURCE	0x40
+#define OPT_DEST	0x80
+#define OPT_ZERO	0x100
+#define OPT_LOGICALIN	0x200
+#define OPT_LOGICALOUT	0x400
+#define OPT_KERNELDATA	0x800 /* This value is also defined in ebtablesd.c */
+#define OPT_COUNT	0x1000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_INCR	0x2000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_DECR	0x4000 /* This value is also defined in libebtc.c */
+
+/* Default command line options. Do not mess around with the already
+ * assigned numbers unless you know what you are doing */
+static struct option ebt_original_options[] =
+{
+	{ "append"         , required_argument, 0, 'A' },
+	{ "insert"         , required_argument, 0, 'I' },
+	{ "delete"         , required_argument, 0, 'D' },
+	{ "list"           , optional_argument, 0, 'L' },
+	{ "Lc"             , no_argument      , 0, 4   },
+	{ "Ln"             , no_argument      , 0, 5   },
+	{ "Lx"             , no_argument      , 0, 6   },
+	{ "Lmac2"          , no_argument      , 0, 12  },
+	{ "zero"           , optional_argument, 0, 'Z' },
+	{ "flush"          , optional_argument, 0, 'F' },
+	{ "policy"         , required_argument, 0, 'P' },
+	{ "in-interface"   , required_argument, 0, 'i' },
+	{ "in-if"          , required_argument, 0, 'i' },
+	{ "logical-in"     , required_argument, 0, 2   },
+	{ "logical-out"    , required_argument, 0, 3   },
+	{ "out-interface"  , required_argument, 0, 'o' },
+	{ "out-if"         , required_argument, 0, 'o' },
+	{ "version"        , no_argument      , 0, 'V' },
+	{ "help"           , no_argument      , 0, 'h' },
+	{ "jump"           , required_argument, 0, 'j' },
+	{ "set-counters"   , required_argument, 0, 'c' },
+	{ "change-counters", required_argument, 0, 'C' },
+	{ "proto"          , required_argument, 0, 'p' },
+	{ "protocol"       , required_argument, 0, 'p' },
+	{ "db"             , required_argument, 0, 'b' },
+	{ "source"         , required_argument, 0, 's' },
+	{ "src"            , required_argument, 0, 's' },
+	{ "destination"    , required_argument, 0, 'd' },
+	{ "dst"            , required_argument, 0, 'd' },
+	{ "table"          , required_argument, 0, 't' },
+	{ "modprobe"       , required_argument, 0, 'M' },
+	{ "new-chain"      , required_argument, 0, 'N' },
+	{ "rename-chain"   , required_argument, 0, 'E' },
+	{ "delete-chain"   , optional_argument, 0, 'X' },
+	{ "atomic-init"    , no_argument      , 0, 7   },
+	{ "atomic-commit"  , no_argument      , 0, 8   },
+	{ "atomic-file"    , required_argument, 0, 9   },
+	{ "atomic-save"    , no_argument      , 0, 10  },
+	{ "init-table"     , no_argument      , 0, 11  },
+	{ "concurrent"     , no_argument      , 0, 13  },
+	{ 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+/* Holds all the data */
+static struct ebt_u_replace *replace;
+
+/* The chosen table */
+static struct ebt_u_table *table;
+
+/* The pointers in here are special:
+ * The struct ebt_target pointer is actually a struct ebt_u_target pointer.
+ * I do not feel like using a union.
+ * We need a struct ebt_u_target pointer because we know the address of the data
+ * they point to won't change. We want to allow that the struct ebt_u_target.t
+ * member can change.
+ * The same holds for the struct ebt_match and struct ebt_watcher pointers */
+static struct ebt_u_entry *new_entry;
+
+
+static int global_option_offset;
+#define OPTION_OFFSET 256
+static struct option *merge_options(struct option *oldopts,
+   const struct option *newopts, unsigned int *options_offset)
+{
+	unsigned int num_old, num_new, i;
+	struct option *merge;
+
+	if (!newopts || !oldopts || !options_offset)
+		ebt_print_bug("merge wrong");
+	for (num_old = 0; oldopts[num_old].name; num_old++);
+	for (num_new = 0; newopts[num_new].name; num_new++);
+
+	global_option_offset += OPTION_OFFSET;
+	*options_offset = global_option_offset;
+
+	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+	if (!merge)
+		ebt_print_memory();
+	memcpy(merge, oldopts, num_old * sizeof(struct option));
+	for (i = 0; i < num_new; i++) {
+		merge[num_old + i] = newopts[i];
+		merge[num_old + i].val += *options_offset;
+	}
+	memset(merge + num_old + num_new, 0, sizeof(struct option));
+	/* Only free dynamically allocated stuff */
+	if (oldopts != ebt_original_options)
+		free(oldopts);
+
+	return merge;
+}
+
+static void merge_match(struct ebt_u_match *m)
+{
+	ebt_options = merge_options
+	   (ebt_options, m->extra_ops, &(m->option_offset));
+}
+
+static void merge_watcher(struct ebt_u_watcher *w)
+{
+	ebt_options = merge_options
+	   (ebt_options, w->extra_ops, &(w->option_offset));
+}
+
+static void merge_target(struct ebt_u_target *t)
+{
+	ebt_options = merge_options
+	   (ebt_options, t->extra_ops, &(t->option_offset));
+}
+
+/* Be backwards compatible, so don't use '+' in kernel */
+#define IF_WILDCARD 1
+static void print_iface(const char *iface)
+{
+	char *c;
+
+	if ((c = strchr(iface, IF_WILDCARD)))
+		*c = '+';
+	printf("%s ", iface);
+	if (c)
+		*c = IF_WILDCARD;
+}
+
+/* We use replace->flags, so we can't use the following values:
+ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
+#define LIST_N    0x04
+#define LIST_C    0x08
+#define LIST_X    0x10
+#define LIST_MAC2 0x20
+
+/* Helper function for list_rules() */
+static void list_em(struct ebt_u_entries *entries)
+{
+	int i, j, space = 0, digits;
+	struct ebt_u_entry *hlp;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t;
+
+	if (replace->flags & LIST_MAC2)
+		ebt_printstyle_mac = 2;
+	else
+		ebt_printstyle_mac = 0;
+	hlp = entries->entries->next;
+	if (replace->flags & LIST_X && entries->policy != EBT_ACCEPT) {
+		printf("ebtables -t %s -P %s %s\n", replace->name,
+		   entries->name, ebt_standard_targets[-entries->policy - 1]);
+	} else if (!(replace->flags & LIST_X)) {
+		printf("\nBridge chain: %s, entries: %d, policy: %s\n",
+		   entries->name, entries->nentries,
+		   ebt_standard_targets[-entries->policy - 1]);
+	}
+
+	if (replace->flags & LIST_N) {
+		i = entries->nentries;
+		while (i > 9) {
+			space++;
+			i /= 10;
+		}
+	}
+
+	for (i = 0; i < entries->nentries; i++) {
+		if (replace->flags & LIST_N) {
+			digits = 0;
+			/* A little work to get nice rule numbers. */
+			j = i + 1;
+			while (j > 9) {
+				digits++;
+				j /= 10;
+			}
+			for (j = 0; j < space - digits; j++)
+				printf(" ");
+			printf("%d. ", i + 1);
+		}
+		if (replace->flags & LIST_X)
+			printf("ebtables -t %s -A %s ",
+			   replace->name, entries->name);
+
+		/* The standard target's print() uses this to find out
+		 * the name of a udc */
+		hlp->replace = replace;
+
+		/* Don't print anything about the protocol if no protocol was
+		 * specified, obviously this means any protocol will do. */
+		if (!(hlp->bitmask & EBT_NOPROTO)) {
+			printf("-p ");
+			if (hlp->invflags & EBT_IPROTO)
+				printf("! ");
+			if (hlp->bitmask & EBT_802_3)
+				printf("Length ");
+			else {
+				struct ethertypeent *ent;
+
+				ent = getethertypebynumber(ntohs(hlp->ethproto));
+				if (!ent)
+					printf("0x%x ", ntohs(hlp->ethproto));
+				else
+					printf("%s ", ent->e_name);
+			}
+		}
+		if (hlp->bitmask & EBT_SOURCEMAC) {
+			printf("-s ");
+			if (hlp->invflags & EBT_ISOURCE)
+				printf("! ");
+			ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk);
+			printf(" ");
+		}
+		if (hlp->bitmask & EBT_DESTMAC) {
+			printf("-d ");
+			if (hlp->invflags & EBT_IDEST)
+				printf("! ");
+			ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk);
+			printf(" ");
+		}
+		if (hlp->in[0] != '\0') {
+			printf("-i ");
+			if (hlp->invflags & EBT_IIN)
+				printf("! ");
+			print_iface(hlp->in);
+		}
+		if (hlp->logical_in[0] != '\0') {
+			printf("--logical-in ");
+			if (hlp->invflags & EBT_ILOGICALIN)
+				printf("! ");
+			print_iface(hlp->logical_in);
+		}
+		if (hlp->logical_out[0] != '\0') {
+			printf("--logical-out ");
+			if (hlp->invflags & EBT_ILOGICALOUT)
+				printf("! ");
+			print_iface(hlp->logical_out);
+		}
+		if (hlp->out[0] != '\0') {
+			printf("-o ");
+			if (hlp->invflags & EBT_IOUT)
+				printf("! ");
+			print_iface(hlp->out);
+		}
+
+		m_l = hlp->m_list;
+		while (m_l) {
+			m = ebt_find_match(m_l->m->u.name);
+			if (!m)
+				ebt_print_bug("Match not found");
+			m->print(hlp, m_l->m);
+			m_l = m_l->next;
+		}
+		w_l = hlp->w_list;
+		while (w_l) {
+			w = ebt_find_watcher(w_l->w->u.name);
+			if (!w)
+				ebt_print_bug("Watcher not found");
+			w->print(hlp, w_l->w);
+			w_l = w_l->next;
+		}
+
+		printf("-j ");
+		if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
+			printf("%s ", hlp->t->u.name);
+		t = ebt_find_target(hlp->t->u.name);
+		if (!t)
+			ebt_print_bug("Target '%s' not found", hlp->t->u.name);
+		t->print(hlp, hlp->t);
+		if (replace->flags & LIST_C) {
+			uint64_t pcnt = hlp->cnt.pcnt;
+			uint64_t bcnt = hlp->cnt.bcnt;
+
+			if (replace->flags & LIST_X)
+				printf("-c %"PRIu64" %"PRIu64, pcnt, bcnt);
+			else
+				printf(", pcnt = %"PRIu64" -- bcnt = %"PRIu64, pcnt, bcnt);
+		}
+		printf("\n");
+		hlp = hlp->next;
+	}
+}
+
+static void print_help()
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	PRINT_VERSION;
+	printf(
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[NX] [chain]\n"
+"ebtables -E old-chain-name new-chain-name\n\n"
+"Commands:\n"
+"--append -A chain             : append to chain\n"
+"--delete -D chain             : delete matching rule from chain\n"
+"--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
+"--change-counters -C chain\n"
+"          [rulenum] pcnt bcnt : change counters of existing rule\n"
+"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+"--list   -L [chain]           : list the rules in a chain or in all chains\n"
+"--flush  -F [chain]           : delete all rules in chain or in all chains\n"
+"--init-table                  : replace the kernel table with the initial table\n"
+"--zero   -Z [chain]           : put counters on zero in chain or in all chains\n"
+"--policy -P chain target      : change policy on chain to target\n"
+"--new-chain -N chain          : create a user defined chain\n"
+"--rename-chain -E old new     : rename a chain\n"
+"--delete-chain -X [chain]     : delete a user defined chain\n"
+"--atomic-commit               : update the kernel w/t table contained in <FILE>\n"
+"--atomic-init                 : put the initial kernel table into <FILE>\n"
+"--atomic-save                 : put the current kernel table into <FILE>\n"
+"--atomic-file file            : set <FILE> to file\n\n"
+"Options:\n"
+"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+"--src    -s [!] address[/mask]: source mac address\n"
+"--dst    -d [!] address[/mask]: destination mac address\n"
+"--in-if  -i [!] name[+]       : network input interface name\n"
+"--out-if -o [!] name[+]       : network output interface name\n"
+"--logical-in  [!] name[+]     : logical bridge input interface name\n"
+"--logical-out [!] name[+]     : logical bridge output interface name\n"
+"--set-counters -c chain\n"
+"          pcnt bcnt           : set the counters of the to be added rule\n"
+"--modprobe -M program         : try to insert modules using this program\n"
+"--concurrent                  : use a file lock to support concurrent scripts\n"
+"--version -V                  : print package version\n\n"
+"Environment variable:\n"
+ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its value"
+"\n\n");
+	m_l = new_entry->m_list;
+	while (m_l) {
+		((struct ebt_u_match *)m_l->m)->help();
+		printf("\n");
+		m_l = m_l->next;
+	}
+	w_l = new_entry->w_list;
+	while (w_l) {
+		((struct ebt_u_watcher *)w_l->w)->help();
+		printf("\n");
+		w_l = w_l->next;
+	}
+	((struct ebt_u_target *)new_entry->t)->help();
+	printf("\n");
+	if (table->help)
+		table->help(ebt_hooknames);
+}
+
+/* Execute command L */
+static void list_rules()
+{
+	int i;
+
+	if (!(replace->flags & LIST_X))
+		printf("Bridge table: %s\n", table->name);
+	if (replace->selected_chain != -1)
+		list_em(ebt_to_chain(replace));
+	else {
+		/* Create new chains and rename standard chains when necessary */
+		if (replace->flags & LIST_X && replace->num_chains > NF_BR_NUMHOOKS) {
+			for (i = NF_BR_NUMHOOKS; i < replace->num_chains; i++)
+				printf("ebtables -t %s -N %s\n", replace->name, replace->chains[i]->name);
+			for (i = 0; i < NF_BR_NUMHOOKS; i++)
+				if (replace->chains[i] && strcmp(replace->chains[i]->name, ebt_hooknames[i]))
+					printf("ebtables -t %s -E %s %s\n", replace->name, ebt_hooknames[i], replace->chains[i]->name);
+		}
+		for (i = 0; i < replace->num_chains; i++)
+			if (replace->chains[i])
+				list_em(replace->chains[i]);
+	}
+}
+
+static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
+{
+	char *colon = strchr(argv, ':'), *buffer;
+
+	if (colon) {
+		*colon = '\0';
+		if (*(colon + 1) == '\0')
+			*rule_nr_end = -1; /* Until the last rule */
+		else {
+			*rule_nr_end = strtol(colon + 1, &buffer, 10);
+			if (*buffer != '\0' || *rule_nr_end == 0)
+				return -1;
+		}
+	}
+	if (colon == argv)
+		*rule_nr = 1; /* Beginning with the first rule */
+	else {
+		*rule_nr = strtol(argv, &buffer, 10);
+		if (*buffer != '\0' || *rule_nr == 0)
+			return -1;
+	}
+	if (!colon)
+		*rule_nr_end = *rule_nr;
+	return 0;
+}
+
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it (too annoying to take the increased
+ * counters in the kernel into account). */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style)
+{
+	char *buffer;
+	int ret = 0;
+
+	if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
+	    (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0'  && argv[optind + 1][1] > '9')))
+		ebt_print_error2("The command -C needs at least 2 arguments");
+	if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
+		if (optind + 3 != argc)
+			ebt_print_error2("No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
+		if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
+			ebt_print_error2("Something is wrong with the rule number specification '%s'", argv[optind]);
+		optind++;
+	}
+
+	if (argv[optind][0] == '+') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+			ebt_print_error2("Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 1;
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else if (argv[optind][0] == '-') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+			ebt_print_error2("Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 2;
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind], &buffer, 10);
+
+	if (*buffer != '\0')
+		goto invalid;
+	optind++;
+	if (argv[optind][0] == '+') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_incr;
+		ret += 3;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else if (argv[optind][0] == '-') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_decr;
+		ret += 6;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10);
+
+	if (*buffer != '\0')
+		goto invalid;
+	optind++;
+	return ret;
+invalid:
+	ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
+}
+
+static int parse_iface(char *iface, char *option)
+{
+	char *c;
+
+	if ((c = strchr(iface, '+'))) {
+		if (*(c + 1) != '\0') {
+			ebt_print_error("Spurious characters after '+' wildcard for '%s'", option);
+			return -1;
+		} else
+			*c = IF_WILDCARD;
+	}
+	return 0;
+}
+
+void ebt_early_init_once()
+{
+	ebt_iterate_matches(merge_match);
+	ebt_iterate_watchers(merge_watcher);
+	ebt_iterate_targets(merge_target);
+}
+
+/* signal handler, installed when the option --concurrent is specified. */
+static void sighandler(int signum)
+{
+	exit(-1);
+}
+
+/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_)
+{
+	char *buffer;
+	int c, i;
+	int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
+	int chcounter = 0; /* Needed for -C */
+	int policy = 0;
+	int rule_nr = 0;
+	int rule_nr_end = 0;
+	struct ebt_u_target *t;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries;
+
+	opterr = 0;
+	ebt_modprobe = NULL;
+
+	replace = replace_;
+
+	/* The daemon doesn't use the environment variable */
+	if (exec_style == EXEC_STYLE_PRG) {
+		buffer = getenv(ATOMIC_ENV_VARIABLE);
+		if (buffer) {
+			replace->filename = malloc(strlen(buffer) + 1);
+			if (!replace->filename)
+				ebt_print_memory();
+			strcpy(replace->filename, buffer);
+			buffer = NULL;
+		}
+	}
+
+	replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */
+	replace->selected_chain = -1;
+	replace->command = 'h';
+
+	if (!new_entry) {
+		new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new_entry)
+			ebt_print_memory();
+	}
+	/* Put some sane values in our new entry */
+	ebt_initialize_entry(new_entry);
+	new_entry->replace = replace;
+
+	/* The scenario induced by this loop makes that:
+	 * '-t'  ,'-M' and --atomic (if specified) have to come
+	 * before '-A' and the like */
+
+	/* Getopt saves the day */
+	while ((c = getopt_long(argc, argv,
+	   "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, NULL)) != -1) {
+		switch (c) {
+
+		case 'A': /* Add a rule */
+		case 'D': /* Delete a rule */
+		case 'C': /* Change counters */
+		case 'P': /* Define policy */
+		case 'I': /* Insert a rule */
+		case 'N': /* Make a user defined chain */
+		case 'E': /* Rename chain */
+		case 'X': /* Delete chain */
+			/* We allow -N chainname -P policy */
+			if (replace->command == 'N' && c == 'P') {
+				replace->command = c;
+				optind--; /* No table specified */
+				goto handle_P;
+			}
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+
+			replace->command = c;
+			replace->flags |= OPT_COMMAND;
+			if (!(replace->flags & OPT_KERNELDATA))
+				ebt_get_kernel_table(replace, 0);
+			if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
+				ebt_print_error2("No chain name specified");
+			if (c == 'N') {
+				if (ebt_get_chainnr(replace, optarg) != -1)
+					ebt_print_error2("Chain %s already exists", optarg);
+				else if (ebt_find_target(optarg))
+					ebt_print_error2("Target with name %s exists", optarg);
+				else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
+					ebt_print_error2("Chain name length can't exceed %d",
+							EBT_CHAIN_MAXNAMELEN - 1);
+				else if (strchr(optarg, ' ') != NULL)
+					ebt_print_error2("Use of ' ' not allowed in chain names");
+				ebt_new_chain(replace, optarg, EBT_ACCEPT);
+				/* This is needed to get -N x -P y working */
+				replace->selected_chain = ebt_get_chainnr(replace, optarg);
+				break;
+			} else if (c == 'X') {
+				if (optind >= argc) {
+					replace->selected_chain = -1;
+					ebt_delete_chain(replace);
+					break;
+				}
+
+				if (optind < argc - 1)
+					ebt_print_error2("No extra options allowed with -X");
+
+				if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1)
+					ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+				ebt_delete_chain(replace);
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				optind++;
+				break;
+			}
+
+			if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1)
+				ebt_print_error2("Chain '%s' doesn't exist", optarg);
+			if (c == 'E') {
+				if (optind >= argc)
+					ebt_print_error2("No new chain name specified");
+				else if (optind < argc - 1)
+					ebt_print_error2("No extra options allowed with -E");
+				else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
+					ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1);
+				else if (ebt_get_chainnr(replace, argv[optind]) != -1)
+					ebt_print_error2("Chain '%s' already exists", argv[optind]);
+				else if (ebt_find_target(argv[optind]))
+					ebt_print_error2("Target with name '%s' exists", argv[optind]);
+				else if (strchr(argv[optind], ' ') != NULL)
+					ebt_print_error2("Use of ' ' not allowed in chain names");
+				ebt_rename_chain(replace, argv[optind]);
+				optind++;
+				break;
+			} else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
+				if (optind != argc - 1)
+					ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]");
+				if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
+					ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]);
+				optind++;
+			} else if (c == 'C') {
+				if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style)) == -1)
+					return -1;
+			} else if (c == 'I') {
+				if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
+					rule_nr = 1;
+				else {
+					rule_nr = strtol(argv[optind], &buffer, 10);
+					if (*buffer != '\0')
+						ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]);
+					optind++;
+				}
+			} else if (c == 'P') {
+handle_P:
+				if (optind >= argc)
+					ebt_print_error2("No policy specified");
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(argv[optind], ebt_standard_targets[i])) {
+						policy = -i -1;
+						if (policy == EBT_CONTINUE)
+							ebt_print_error2("Wrong policy '%s'", argv[optind]);
+						break;
+					}
+				if (i == NUM_STANDARD_TARGETS)
+					ebt_print_error2("Unknown policy '%s'", argv[optind]);
+				optind++;
+			}
+			break;
+		case 'L': /* List */
+		case 'F': /* Flush */
+		case 'Z': /* Zero counters */
+			if (c == 'Z') {
+				if ((replace->flags & OPT_ZERO) || (replace->flags & OPT_COMMAND && replace->command != 'L'))
+print_zero:
+					ebt_print_error2("Command -Z only allowed together with command -L");
+				replace->flags |= OPT_ZERO;
+			} else {
+				if (replace->flags & OPT_COMMAND)
+					ebt_print_error2("Multiple commands are not allowed");
+				replace->command = c;
+				replace->flags |= OPT_COMMAND;
+				if (replace->flags & OPT_ZERO && c != 'L')
+					goto print_zero;
+			}
+
+#ifdef SILENT_DAEMON
+			if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-L not supported in daemon mode");
+#endif
+
+			if (!(replace->flags & OPT_KERNELDATA))
+				ebt_get_kernel_table(replace, 0);
+			i = -1;
+			if (optind < argc && argv[optind][0] != '-') {
+				if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
+					ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+				optind++;
+			}
+			if (i != -1) {
+				if (c == 'Z')
+					zerochain = i;
+				else
+					replace->selected_chain = i;
+			}
+			break;
+		case 'V': /* Version */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->command = 'V';
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
+			PRINT_VERSION;
+			exit(0);
+		case 'M': /* Modprobe */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the -M option earlier");
+			free(ebt_modprobe);
+			ebt_modprobe = optarg;
+			break;
+		case 'h': /* Help */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-h not supported in daemon mode");
+#endif
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->command = 'h';
+
+			/* All other arguments should be extension names */
+			while (optind < argc) {
+				struct ebt_u_match *m;
+				struct ebt_u_watcher *w;
+
+				if (!strcasecmp("list_extensions", argv[optind])) {
+					ebt_list_extensions();
+					exit(0);
+				}
+				if ((m = ebt_find_match(argv[optind])))
+					ebt_add_match(new_entry, m);
+				else if ((w = ebt_find_watcher(argv[optind])))
+					ebt_add_watcher(new_entry, w);
+				else {
+					if (!(t = ebt_find_target(argv[optind])))
+						ebt_print_error2("Extension '%s' not found", argv[optind]);
+					if (replace->flags & OPT_JUMP)
+						ebt_print_error2("Sorry, you can only see help for one target extension at a time");
+					replace->flags |= OPT_JUMP;
+					new_entry->t = (struct ebt_entry_target *)t;
+				}
+				optind++;
+			}
+			break;
+		case 't': /* Table */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the -t option first");
+			ebt_check_option2(&(replace->flags), OPT_TABLE);
+			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+				ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1);
+			strcpy(replace->name, optarg);
+			break;
+		case 'i': /* Input interface */
+		case 2  : /* Logical input interface */
+		case 'o': /* Output interface */
+		case 3  : /* Logical output interface */
+		case 'j': /* Target */
+		case 'p': /* Net family protocol */
+		case 's': /* Source mac */
+		case 'd': /* Destination mac */
+		case 'c': /* Set counters */
+			if (!OPT_COMMANDS)
+				ebt_print_error2("No command specified");
+			if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I' && replace->command != 'C')
+				ebt_print_error2("Command and option do not match");
+			if (c == 'i') {
+				ebt_check_option2(&(replace->flags), OPT_IN);
+				if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
+					ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IIN;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+big_iface_length:
+					ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1);
+				strcpy(new_entry->in, optarg);
+				if (parse_iface(new_entry->in, "-i"))
+					return -1;
+				break;
+			} else if (c == 2) {
+				ebt_check_option2(&(replace->flags), OPT_LOGICALIN);
+				if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
+					ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ILOGICALIN;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->logical_in, optarg);
+				if (parse_iface(new_entry->logical_in, "--logical-in"))
+					return -1;
+				break;
+			} else if (c == 'o') {
+				ebt_check_option2(&(replace->flags), OPT_OUT);
+				if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
+					ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IOUT;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->out, optarg);
+				if (parse_iface(new_entry->out, "-o"))
+					return -1;
+				break;
+			} else if (c == 3) {
+				ebt_check_option2(&(replace->flags), OPT_LOGICALOUT);
+				if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
+					ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ILOGICALOUT;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->logical_out, optarg);
+				if (parse_iface(new_entry->logical_out, "--logical-out"))
+					return -1;    
+				break;
+			} else if (c == 'j') {
+				ebt_check_option2(&(replace->flags), OPT_JUMP);
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(optarg, ebt_standard_targets[i])) {
+						t = ebt_find_target(EBT_STANDARD_TARGET);
+						((struct ebt_standard_target *) t->t)->verdict = -i - 1;
+						break;
+					}
+				if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) {
+					ebt_print_error2("Return target only for user defined chains");
+				} else if (i != NUM_STANDARD_TARGETS)
+					break;
+
+				if ((i = ebt_get_chainnr(replace, optarg)) != -1) {
+					if (i < NF_BR_NUMHOOKS)
+						ebt_print_error2("Don't jump to a standard chain");
+					t = ebt_find_target(EBT_STANDARD_TARGET);
+					((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS;
+					break;
+				} else {
+					/* Must be an extension then */
+					struct ebt_u_target *t;
+
+					t = ebt_find_target(optarg);
+					/* -j standard not allowed either */
+					if (!t || t == (struct ebt_u_target *)new_entry->t)
+						ebt_print_error2("Illegal target name '%s'", optarg);
+					new_entry->t = (struct ebt_entry_target *)t;
+					ebt_find_target(EBT_STANDARD_TARGET)->used = 0;
+					t->used = 1;
+				}
+				break;
+			} else if (c == 's') {
+				ebt_check_option2(&(replace->flags), OPT_SOURCE);
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ISOURCE;
+
+				if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk))
+					ebt_print_error2("Problem with specified source mac '%s'", optarg);
+				new_entry->bitmask |= EBT_SOURCEMAC;
+				break;
+			} else if (c == 'd') {
+				ebt_check_option2(&(replace->flags), OPT_DEST);
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IDEST;
+
+				if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk))
+					ebt_print_error2("Problem with specified destination mac '%s'", optarg);
+				new_entry->bitmask |= EBT_DESTMAC;
+				break;
+			} else if (c == 'c') {
+				ebt_check_option2(&(replace->flags), OPT_COUNT);
+				if (ebt_check_inverse2(optarg))
+					ebt_print_error2("Unexpected '!' after -c");
+				if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
+					ebt_print_error2("Option -c needs 2 arguments");
+
+				new_entry->cnt.pcnt = strtoull(optarg, &buffer, 10);
+				if (*buffer != '\0')
+					ebt_print_error2("Packet counter '%s' invalid", optarg);
+				new_entry->cnt.bcnt = strtoull(argv[optind], &buffer, 10);
+				if (*buffer != '\0')
+					ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
+				optind++;
+				break;
+			}
+			ebt_check_option2(&(replace->flags), OPT_PROTOCOL);
+			if (ebt_check_inverse2(optarg))
+				new_entry->invflags |= EBT_IPROTO;
+
+			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+			i = strtol(optarg, &buffer, 16);
+			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+				ebt_print_error2("Problem with the specified protocol");
+			if (*buffer != '\0') {
+				struct ethertypeent *ent;
+
+				if (!strcasecmp(optarg, "LENGTH")) {
+					new_entry->bitmask |= EBT_802_3;
+					break;
+				}
+				ent = getethertypebyname(optarg);
+				if (!ent)
+					ebt_print_error2("Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
+				new_entry->ethproto = ent->e_ethertype;
+			} else
+				new_entry->ethproto = i;
+
+			if (new_entry->ethproto < 0x0600)
+				ebt_print_error2("Sorry, protocols have values above or equal to 0x0600");
+			break;
+		case 4  : /* Lc */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lc is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_C);
+			if (replace->command != 'L')
+				ebt_print_error("Use --Lc with -L");
+			replace->flags |= LIST_C;
+			break;
+		case 5  : /* Ln */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Ln is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_N);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Ln with -L");
+			if (replace->flags & LIST_X)
+				ebt_print_error2("--Lx is not compatible with --Ln");
+			replace->flags |= LIST_N;
+			break;
+		case 6  : /* Lx */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lx is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_X);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Lx with -L");
+			if (replace->flags & LIST_N)
+				ebt_print_error2("--Lx is not compatible with --Ln");
+			replace->flags |= LIST_X;
+			break;
+		case 12 : /* Lmac2 */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error("--Lmac2 is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_MAC2);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Lmac2 with -L");
+			replace->flags |= LIST_MAC2;
+			break;
+		case 8 : /* atomic-commit */
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--atomic-commit is not supported in daemon mode");
+			replace->command = c;
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->flags |= OPT_COMMAND;
+			if (!replace->filename)
+				ebt_print_error2("No atomic file specified");
+			/* Get the information from the file */
+			ebt_get_table(replace, 0);
+			/* We don't want the kernel giving us its counters,
+			 * they would overwrite the counters extracted from
+			 * the file */
+			replace->num_counters = 0;
+			/* Make sure the table will be written to the kernel */
+			free(replace->filename);
+			replace->filename = NULL;
+			break;
+		case 7 : /* atomic-init */
+		case 10: /* atomic-save */
+		case 11: /* init-table */
+			if (exec_style == EXEC_STYLE_DAEMON) {
+				if (c == 7) {
+					ebt_print_error2("--atomic-init is not supported in daemon mode");
+				} else if (c == 10)
+					ebt_print_error2("--atomic-save is not supported in daemon mode");
+				ebt_print_error2("--init-table is not supported in daemon mode");
+			}
+			replace->command = c;
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			if (c != 11 && !replace->filename)
+				ebt_print_error2("No atomic file specified");
+			replace->flags |= OPT_COMMAND;
+			{
+				char *tmp = replace->filename;
+
+				/* Get the kernel table */
+				replace->filename = NULL;
+				ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
+				replace->filename = tmp;
+			}
+			break;
+		case 9 : /* atomic */
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--atomic is not supported in daemon mode");
+			if (OPT_COMMANDS)
+				ebt_print_error2("--atomic has to come before the command");
+			/* A possible memory leak here, but this is not
+			 * executed in daemon mode */
+			replace->filename = (char *)malloc(strlen(optarg) + 1);
+			strcpy(replace->filename, optarg);
+			break;
+		case 13 : /* concurrent */
+			signal(SIGINT, sighandler);
+			signal(SIGTERM, sighandler);
+			use_lockfd = 1;
+			break;
+		case 1 :
+			if (!strcmp(optarg, "!"))
+				ebt_check_inverse2(optarg);
+			else
+				ebt_print_error2("Bad argument : '%s'", optarg);
+			/* ebt_check_inverse() did optind++ */
+			optind--;
+			continue;
+		default:
+			/* Is it a target option? */
+			t = (struct ebt_u_target *)new_entry->t;
+			if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				goto check_extension;
+			}
+
+			/* Is it a match_option? */
+			for (m = ebt_matches; m; m = m->next)
+				if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
+					break;
+
+			if (m != NULL) {
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				if (m->used == 0) {
+					ebt_add_match(new_entry, m);
+					m->used = 1;
+				}
+				goto check_extension;
+			}
+
+			/* Is it a watcher option? */
+			for (w = ebt_watchers; w; w = w->next)
+				if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w))
+					break;
+
+			if (w == NULL && c == '?')
+				ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
+			else if (w == NULL) {
+				if (!strcmp(t->name, "standard"))
+					ebt_print_error2("Unknown argument: don't forget the -t option");
+				else
+					ebt_print_error2("Target-specific option does not correspond with specified target");
+			}
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			if (w->used == 0) {
+				ebt_add_watcher(new_entry, w);
+				w->used = 1;
+			}
+check_extension:
+			if (replace->command != 'A' && replace->command != 'I' &&
+			    replace->command != 'D' && replace->command != 'C')
+				ebt_print_error2("Extensions only for -A, -I, -D and -C");
+		}
+		ebt_invert = 0;
+	}
+
+	/* Just in case we didn't catch an error */
+	if (ebt_errormsg[0] != '\0')
+		return -1;
+
+	if (!(table = ebt_find_table(replace->name)))
+		ebt_print_error2("Bad table name");
+
+	if (replace->command == 'h' && !(replace->flags & OPT_ZERO)) {
+		print_help();
+		if (exec_style == EXEC_STYLE_PRG)
+			exit(0);
+	}
+
+	/* Do the final checks */
+	if (replace->command == 'A' || replace->command == 'I' ||
+	   replace->command == 'D' || replace->command == 'C') {
+		/* This will put the hook_mask right for the chains */
+		ebt_check_for_loops(replace);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		entries = ebt_to_chain(replace);
+		m_l = new_entry->m_list;
+		w_l = new_entry->w_list;
+		t = (struct ebt_u_target *)new_entry->t;
+		while (m_l) {
+			m = (struct ebt_u_match *)(m_l->m);
+			m->final_check(new_entry, m->m, replace->name,
+			   entries->hook_mask, 0);
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			m_l = m_l->next;
+		}
+		while (w_l) {
+			w = (struct ebt_u_watcher *)(w_l->w);
+			w->final_check(new_entry, w->w, replace->name,
+			   entries->hook_mask, 0);
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			w_l = w_l->next;
+		}
+		t->final_check(new_entry, t->t, replace->name,
+		   entries->hook_mask, 0);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	}
+	/* So, the extensions can work with the host endian.
+	 * The kernel does not have to do this of course */
+	new_entry->ethproto = htons(new_entry->ethproto);
+
+	if (replace->command == 'P') {
+		if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN)
+			ebt_print_error2("Policy RETURN only allowed for user defined chains");
+		ebt_change_policy(replace, policy);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	} else if (replace->command == 'L') {
+		list_rules();
+		if (!(replace->flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
+			exit(0);
+	}
+	if (replace->flags & OPT_ZERO) {
+		replace->selected_chain = zerochain;
+		ebt_zero_counters(replace);
+	} else if (replace->command == 'F') {
+		ebt_flush_chains(replace);
+	} else if (replace->command == 'A' || replace->command == 'I') {
+		ebt_add_rule(replace, new_entry, rule_nr);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		/* Makes undoing the add easier (jumps to delete_the_rule) */
+		if (rule_nr <= 0)
+			rule_nr--;
+		rule_nr_end = rule_nr;
+
+		/* a jump to a udc requires checking for loops */
+		if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) &&
+		((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) {
+			/* FIXME: this can be done faster */
+			ebt_check_for_loops(replace);
+			if (ebt_errormsg[0] != '\0')
+				goto delete_the_rule;
+		}
+
+		/* Do the final_check(), for all entries.
+		 * This is needed when adding a rule that has a chain target */
+		i = -1;
+		while (++i != replace->num_chains) {
+			struct ebt_u_entry *e;
+
+			entries = replace->chains[i];
+			if (!entries) {
+				if (i < NF_BR_NUMHOOKS)
+					continue;
+				else
+					ebt_print_bug("whoops\n");
+			}
+			e = entries->entries->next;
+			while (e != entries->entries) {
+				/* Userspace extensions use host endian */
+				e->ethproto = ntohs(e->ethproto);
+				ebt_do_final_checks(replace, e, entries);
+				if (ebt_errormsg[0] != '\0')
+					goto delete_the_rule;
+				e->ethproto = htons(e->ethproto);
+				e = e->next;
+			}
+		}
+		/* Don't reuse the added rule */
+		new_entry = NULL;
+	} else if (replace->command == 'D') {
+delete_the_rule:
+		ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	} else if (replace->command == 'C') {
+		ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	}
+	/* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+	 * --init-table fall through */
+
+	if (ebt_errormsg[0] != '\0')
+		return -1;
+	if (table->check)
+		table->check(replace);
+
+	if (exec_style == EXEC_STYLE_PRG) {/* Implies ebt_errormsg[0] == '\0' */
+		ebt_deliver_table(replace);
+
+		if (replace->nentries)
+			ebt_deliver_counters(replace);
+	}
+	return 0;
+}
diff --git a/userspace/ebtables2/ebtables.spec b/userspace/ebtables2/ebtables.spec
new file mode 100644
index 0000000..c57fbde
--- /dev/null
+++ b/userspace/ebtables2/ebtables.spec
@@ -0,0 +1,83 @@
+# spec file originally from Dag Wieers, altered by Bart De Schuymer
+
+%define _sbindir /usr/local/sbin
+%define _mysysconfdir %{_sysconfdir}/sysconfig
+
+Summary: Ethernet Bridge frame table administration tool
+Name: ebtables
+Version: $(VERSION)
+Release: $(RELEASE)
+License: GPL
+Group: System Environment/Base
+URL: http://ebtables.sourceforge.net/
+
+Packager: Bart De Schuymer <bdschuym@pandora.be>
+
+Source: http://dl.sf.net/ebtables/ebtables-v%{version}-%{release}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+Ethernet bridge tables is a firewalling tool to transparantly filter network
+traffic passing a bridge. The filtering possibilities are limited to link
+layer filtering and some basic filtering on higher network layers.
+
+The ebtables tool can be used together with the other Linux filtering tools,
+like iptables. There are no incompatibility issues.
+
+%prep
+%setup -n ebtables-v%{version}-%{release}
+
+%build
+%{__make} %{?_smp_mflags} \
+	CFLAGS="%{optflags}"
+
+%install
+%{__rm} -rf %{buildroot}
+%{__install} -D -m0755 ebtables %{buildroot}%{_sbindir}/ebtables
+%{__install} -D -m0755 ebtables-restore %{buildroot}%{_sbindir}/ebtables-restore
+%{__install} -D -m0644 ethertypes %{buildroot}%{_sysconfdir}/ethertypes
+%{__install} -D -m0644 ebtables.8 %{buildroot}%{_mandir}/man8/ebtables.8
+%{__mkdir} -p %{buildroot}%{_libdir}/ebtables/
+%{__mkdir} -p %{buildroot}%{_sbindir}
+%{__mkdir} -p %{buildroot}%{_initrddir}
+%{__mkdir} -p %{buildroot}%{_mysysconfdir}
+%{__install} -m0755 extensions/*.so %{buildroot}%{_libdir}/ebtables/
+%{__install} -m0755 *.so %{buildroot}%{_libdir}/ebtables/
+export __iets=`printf %{_sbindir} | sed 's/\\//\\\\\\//g'`
+export __iets2=`printf %{_mysysconfdir} | sed 's/\\//\\\\\\//g'`
+sed -i "s/__EXEC_PATH__/$__iets/g" ebtables-save
+%{__install} -m 0755 -o root -g root ebtables-save %{buildroot}%{_sbindir}/ebtables-save
+sed -i "s/__EXEC_PATH__/$__iets/g" ebtables.sysv; sed -i "s/__SYSCONFIG__/$__iets2/g" ebtables.sysv
+%{__install} -m 0755 -o root -g root ebtables.sysv %{buildroot}%{_initrddir}/ebtables
+sed -i "s/__SYSCONFIG__/$__iets2/g" ebtables-config
+%{__install} -m 0600 -o root -g root ebtables-config %{buildroot}%{_mysysconfdir}/ebtables-config
+unset __iets
+unset __iets2
+
+%clean
+%{__rm} -rf %{buildroot}
+
+%post
+/sbin/chkconfig --add ebtables
+
+%preun
+if [ $1 -eq 0 ]; then
+	/sbin/service ebtables stop &>/dev/null || :
+	/sbin/chkconfig --del ebtables
+fi
+
+%files
+%defattr(-, root, root, 0755)
+%doc ChangeLog COPYING INSTALL THANKS
+%doc %{_mandir}/man8/ebtables.8*
+%config %{_sysconfdir}/ethertypes
+%config %{_mysysconfdir}/ebtables-config
+%config %{_initrddir}/ebtables
+%{_sbindir}/ebtables
+%{_sbindir}/ebtables-save
+%{_sbindir}/ebtables-restore
+%{_libdir}/ebtables/
+
+%changelog
+* Mon Nov 07 2005 Bart De Schuymer <bdschuym@pandora.be> - 2.0.8-rc1
+- Initial package.
diff --git a/userspace/ebtables2/ebtables.sysv b/userspace/ebtables2/ebtables.sysv
new file mode 100644
index 0000000..b6848f1
--- /dev/null
+++ b/userspace/ebtables2/ebtables.sysv
@@ -0,0 +1,145 @@
+#!/bin/bash
+#
+# init script for the Ethernet Bridge filter tables
+#
+# Written by Dag Wieers <dag@wieers.com>
+# Modified by Rok Papez <rok.papez@arnes.si>
+#             Bart De Schuymer <bdschuym@pandora.be>
+#
+# chkconfig: - 15 85
+# description: Ethernet Bridge filtering tables
+#
+# config: __SYSCONFIG__/ebtables         (text)
+#         __SYSCONFIG__/ebtables.<table> (binary)
+
+source /etc/init.d/functions
+source /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x __EXEC_PATH__/ebtables ] || exit 1
+[ -x __EXEC_PATH__/ebtables-save ] || exit 1
+[ -x __EXEC_PATH__/ebtables-restore ] || exit 1
+
+RETVAL=0
+prog="ebtables"
+desc="Ethernet bridge filtering"
+umask 0077
+
+#default configuration
+EBTABLES_TEXT_FORMAT="yes"
+EBTABLES_BINARY_FORMAT="yes"
+EBTABLES_MODULES_UNLOAD="yes"
+EBTABLES_SAVE_ON_STOP="no"
+EBTABLES_SAVE_ON_RESTART="no"
+EBTABLES_SAVE_COUNTER="no"
+
+config=__SYSCONFIG__/$prog-config
+[ -f "$config" ] && . "$config"
+
+start() {
+	echo -n $"Starting $desc ($prog): "
+	if [ "$EBTABLES_BINARY_FORMAT" = "yes" ]; then
+		for table in $(ls __SYSCONFIG__/ebtables.* 2>/dev/null | sed -e 's/.*ebtables\.//' -e '/save/d' ); do
+			__EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table --atomic-commit || RETVAL=1
+		done
+	else
+		__EXEC_PATH__/ebtables-restore < /etc/sysconfig/ebtables || RETVAL=1
+	fi
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog startup"
+		rm -f /var/lock/subsys/$prog
+	else
+		failure "$prog startup"
+	fi
+	echo
+}
+
+stop() {
+	echo -n $"Stopping $desc ($prog): "
+	for table in $(grep '^ebtable_' /proc/modules | sed -e 's/ebtable_\([^ ]*\).*/\1/'); do
+		__EXEC_PATH__/ebtables -t $table --init-table || RETVAL=1
+	done
+
+	if [ "$EBTABLES_MODULES_UNLOAD" = "yes" ]; then
+		for mod in $(grep -E '^(ebt|ebtable)_' /proc/modules | cut -f1 -d' ') ebtables; do
+			rmmod $mod 2> /dev/null
+		done
+	fi
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog shutdown"
+		rm -f /var/lock/subsys/$prog
+	else
+		failure "$prog shutdown"
+	fi
+	echo
+}
+
+restart() {
+	stop
+	start
+}
+
+save() {
+	echo -n $"Saving $desc ($prog): "
+	if [ "$EBTABLES_TEXT_FORMAT" = "yes" ]; then
+		if [ -e __SYSCONFIG__/ebtables ]; then
+			chmod 0600 __SYSCONFIG__/ebtables
+			mv -f __SYSCONFIG__/ebtables __SYSCONFIG__/ebtables.save
+		fi
+		__EXEC_PATH__/ebtables-save > __SYSCONFIG__/ebtables || RETVAL=1
+	fi
+	if [ "$EBTABLES_BINARY_FORMAT" = "yes" ]; then
+		rm -f __SYSCONFIG__/ebtables.*.save
+		for oldtable in $(ls __SYSCONFIG__/ebtables.* 2>/dev/null | grep -vF 'ebtables.save'); do
+			chmod 0600 $oldtable
+			mv -f $oldtable $oldtable.save
+		done
+		for table in $(grep '^ebtable_' /proc/modules | sed -e 's/ebtable_\([^ ]*\).*/\1/'); do
+			__EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table --atomic-save || RETVAL=1
+			if [ "$EBTABLES_SAVE_COUNTER" = "no" ]; then
+				__EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table -Z || RETVAL=1
+			fi
+		done
+	fi
+
+	if [ $RETVAL -eq 0 ]; then
+		success "$prog saved"
+	else
+		failure "$prog saved"
+	fi
+	echo
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	[ "$EBTABLES_SAVE_ON_STOP" = "yes" ] && save
+	stop
+	;;
+  restart|reload)
+	[ "$EBTABLES_SAVE_ON_RESTART" = "yes" ] && save
+	restart
+	;;
+  condrestart)
+	[ -e /var/lock/subsys/$prog ] && restart
+	RETVAL=$?
+	;;
+  save)
+	save
+	;;
+  status)
+	__EXEC_PATH__/ebtables-save
+	RETVAL=$?
+	;;
+  *)
+	echo $"Usage $0 {start|stop|restart|condrestart|save|status}"
+	RETVAL=1
+esac
+
+exit $RETVAL
diff --git a/userspace/ebtables2/ebtablesd.c b/userspace/ebtables2/ebtablesd.c
new file mode 100644
index 0000000..062a2d6
--- /dev/null
+++ b/userspace/ebtables2/ebtablesd.c
@@ -0,0 +1,374 @@
+/*
+ * ebtablesd.c, January 2005
+ *
+ * Author: Bart De Schuymer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "include/ebtables_u.h"
+
+#define OPT_KERNELDATA	0x800 /* Also defined in ebtables.c */
+
+static struct ebt_u_replace replace[3];
+#define OPEN_METHOD_FILE 1
+#define OPEN_METHOD_KERNEL 2
+static int open_method[3];
+void ebt_early_init_once();
+
+static void sigpipe_handler(int sig)
+{
+}
+static void copy_table_names()
+{
+	strcpy(replace[0].name, "filter");
+	strcpy(replace[1].name, "nat");
+	strcpy(replace[2].name, "broute");
+}
+
+int main(int argc_, char *argv_[])
+{
+	char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir",
+	     mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR,
+	     cmdline[EBTD_CMDLINE_MAXLN];
+	int readfd, base = 0, offset = 0, n = 0, ret = 0, quotemode = 0;
+
+	/* Make sure the pipe directory exists */
+	args[0] = name;
+	args[1] = mkdir_option;
+	args[2] = mkdir_dir;
+	args[3] = NULL;
+	switch (fork()) {
+	case 0:
+		execvp(args[0], args);
+
+		/* Not usually reached */
+		exit(0);
+	case -1:
+		return -1;
+
+	default: /* Parent */
+		wait(NULL);
+	}
+
+	if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) {
+		printf("Error creating FIFO " EBTD_PIPE "\n");
+		ret = -1;
+		goto do_exit;
+	}
+
+	if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) {
+		perror("open");
+		ret = -1;
+		goto do_exit;
+	}
+
+	if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) {
+		perror("signal");
+		ret = -1;
+		goto do_exit;
+	}
+
+	ebt_silent = 1;
+
+	copy_table_names();
+	ebt_early_init_once();
+
+	while (1) {
+		int n2, i, argc, table_nr, ntot;
+
+		/* base == 0 */
+		ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1);
+		if (ntot <= 0)
+			continue;
+		ntot += offset;
+continue_read:
+		/* Put '\0' between arguments. */
+		for (; offset < ntot; n++, offset++) {
+			if (cmdline[offset] == '\"') {
+				quotemode ^= 1;
+				cmdline[offset] = '\0';
+			} else if (!quotemode && cmdline[offset] == ' ') {
+				cmdline[offset] = '\0';
+			} else if (cmdline[offset] == '\n') {
+				if (quotemode)
+					ebt_print_error("ebtablesd: wrong number of \" delimiters");
+				cmdline[offset] = '\0';
+				break;
+			}
+		}
+		if (n == 0) {
+			if (offset == ntot) {
+				/* The ntot bytes were parsed and ended with '\n' */
+				base = 0;
+				offset = 0;
+				continue;
+			}
+			offset++;
+			base = offset;
+			n = 0;
+			goto continue_read;
+		}
+		if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */
+			if (base == 0) {
+				ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1);
+				goto write_msg;
+			}
+			memmove(cmdline, cmdline+base+offset, ntot-offset);
+			offset -= base;
+			offset++;
+			base = 0;
+			continue;
+		}
+
+		table_nr = 0;
+		n2 = 0;
+		argc = 0;
+		while (n2 < n && argc < EBTD_ARGC_MAX) {
+			if (*(cmdline + base + n2) == '\0') {
+				n2++;
+				continue;
+			}
+			argv[argc++] = cmdline + base + n2;
+			n2 += strlen(cmdline + base + n2) + 1;
+		}
+		offset++; /* Move past the '\n' */
+		base = offset;
+
+		if (argc > EBTD_ARGC_MAX) {
+			ebt_print_error("ebtablesd: maximum %d arguments "
+			                "allowed", EBTD_ARGC_MAX - 1);
+			goto write_msg;
+		}
+		if (argc == 1) {
+			ebt_print_error("ebtablesd: no arguments");
+			goto write_msg;
+		}
+
+		/* Parse the options */
+		if (!strcmp(argv[1], "-t")) {
+			if (argc < 3) {
+				ebt_print_error("ebtablesd: -t but no table");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			table_nr = i;
+		} else if (!strcmp(argv[1], "free")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command free "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			ebt_cleanup_replace(&replace[i]);
+			copy_table_names();
+			replace[i].flags &= ~OPT_KERNELDATA;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "open")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command open "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			if (!ebt_get_kernel_table(&replace[i], 0)) {
+				replace[i].flags |= OPT_KERNELDATA;
+				open_method[i] = OPEN_METHOD_KERNEL;
+			}
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fopen")) {
+			struct ebt_u_replace tmp;
+
+			memset(&tmp, 0, sizeof(tmp));
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command fopen "
+				                "needs exactly two arguments");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			tmp.filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!tmp.filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(tmp.filename, argv[3]);
+			strcpy(tmp.name, "filter");
+			tmp.command = 'L'; /* Make sure retrieve_from_file()
+			                    * doesn't complain about wrong
+			                    * table name */
+
+			ebt_get_kernel_table(&tmp, 0);
+			free(tmp.filename);
+			tmp.filename = NULL;
+			if (ebt_errormsg[0] != '\0')
+				goto write_msg;
+
+			if (strcmp(tmp.name, argv[2])) {
+				ebt_print_error("ebtablesd: opened file with "
+				                "wrong table name '%s'", tmp.name);
+				ebt_cleanup_replace(&tmp);
+				goto write_msg;
+			}
+			replace[i] = tmp;
+			replace[i].command = '\0';
+			replace[i].flags |= OPT_KERNELDATA;
+			open_method[i] = OPEN_METHOD_FILE;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "commit")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			/* The counters from the kernel are useless if we 
+			 * didn't start from a kernel table */
+			if (open_method[i] == OPEN_METHOD_FILE)
+				replace[i].num_counters = 0;
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i]);
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fcommit")) {
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly two argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			replace[i].filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!replace[i].filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(replace[i].filename, argv[3]);
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i]);
+			free(replace[i].filename);
+			replace[i].filename = NULL;
+			goto write_msg;
+		}else if (!strcmp(argv[1], "quit")) {
+			if (argc != 2) {
+				ebt_print_error("ebtablesd: command quit does "
+				                "not take any arguments");
+				goto write_msg;
+			}
+			break;
+		}
+		if (!(replace[table_nr].flags & OPT_KERNELDATA)) {
+			ebt_print_error("ebtablesd: table %s has not been "
+			                "opened", replace[table_nr].name);
+			goto write_msg;
+		}
+		optind = 0; /* Setting optind = 1 causes serious annoyances */
+		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
+		ebt_reinit_extensions();
+write_msg:
+#ifndef SILENT_DAEMON
+		if (ebt_errormsg[0] != '\0')
+			printf("%s.\n", ebt_errormsg);
+#endif
+		ebt_errormsg[0]= '\0';
+		n = 0;
+		goto continue_read;
+	}
+do_exit:
+	unlink(EBTD_PIPE);
+	
+	return 0;
+}
diff --git a/userspace/ebtables2/ebtablesu.c b/userspace/ebtables2/ebtablesu.c
new file mode 100644
index 0000000..3cee145
--- /dev/null
+++ b/userspace/ebtables2/ebtablesu.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static void print_help()
+{
+	printf("ebtablesu v"PROGVERSION" ("PROGDATE")\n");
+	printf(
+"Usage:\n"
+"ebtablesu open table         : copy the kernel table\n"
+"ebtablesu fopen table file   : copy the table from the specified file\n"
+"ebtablesu free table         : remove the table from memory\n"
+"ebtablesu commit table       : commit the table to the kernel\n"
+"ebtablesu fcommit table file : commit the table to the specified file\n\n"
+"ebtablesu <ebtables options> : the ebtables specifications\n"
+"                               use spaces only to separate options and commands\n"
+"For the ebtables options, see\n# ebtables -h\nor\n# man ebtables\n"
+	);
+}
+int main(int argc, char *argv[])
+{
+	char *arguments, *pos;
+	int i, writefd, len = 0;
+
+	if (argc > EBTD_ARGC_MAX) {
+		fprintf(stderr, "ebtablesd accepts at most %d arguments, %d "
+		        "arguments were specified. If you need this many "
+		        "arguments, recompile this tool with a higher value for"
+		        " EBTD_ARGC_MAX.\n", EBTD_ARGC_MAX - 1, argc - 1);
+		return -1;
+	} else if (argc == 1) {
+		fprintf(stderr, "At least one argument is needed.\n");
+		print_help();
+		exit(0);
+	}
+
+	for (i = 0; i < argc; i++)
+		len += strlen(argv[i]);
+	/* Don't forget '\0' */
+	len += argc;
+	if (len > EBTD_CMDLINE_MAXLN) {
+		fprintf(stderr, "ebtablesd has a maximum command line argument "
+		       "length of %d, an argument length of %d was received. "
+		       "If a smaller length is unfeasible, recompile this tool "
+		       "with a higher value for EBTD_CMDLINE_MAXLN.\n",
+		       EBTD_CMDLINE_MAXLN, len);
+		return -1;
+	}
+
+	if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+		if (argc != 2) {
+			fprintf(stderr, "%s does not accept options.\n", argv[1]);
+			return -1;
+		}
+		print_help();
+		exit(0);
+	}
+
+	if (!(arguments = (char *)malloc(len))) {
+		fprintf(stderr, "ebtablesu: out of memory.\n");
+		return -1;
+	}
+
+	if ((writefd = open(EBTD_PIPE, O_WRONLY, 0)) == -1) {
+		fprintf(stderr, "Could not open the pipe, perhaps ebtablesd is "
+		        "not running or you don't have write permission (try "
+		        "running as root).\n");
+		return -1;
+	}
+
+	pos = arguments;
+	for (i = 0; i < argc; i++) {
+		strcpy(pos, argv[i]);
+		pos += strlen(argv[i]);
+		*(pos++) = ' ';
+	}
+
+	*(pos-1) = '\n';
+	if (write(writefd, arguments, len) == -1) {
+		perror("write");
+		return -1;
+	}
+	return 0;
+}
diff --git a/userspace/ebtables2/ethertypes b/userspace/ebtables2/ethertypes
new file mode 100644
index 0000000..813177b
--- /dev/null
+++ b/userspace/ebtables2/ethertypes
@@ -0,0 +1,39 @@
+#
+# Ethernet frame types
+#		This file describes some of the various Ethernet
+#		protocol types that are used on Ethernet networks.
+#
+# This list could be found on:
+#         http://www.iana.org/assignments/ethernet-numbers
+#         http://www.iana.org/assignments/ieee-802-numbers
+#
+# <name>    <hexnumber> <alias1>...<alias35> #Comment
+#
+IPv4	 	0800  	ip ip4 		# Internet IP (IPv4)
+X25		0805
+ARP		0806	ether-arp	#
+FR_ARP		0808    		# Frame Relay ARP        [RFC1701]
+BPQ		08FF			# G8BPQ AX.25 Ethernet Packet
+DEC		6000			# DEC Assigned proto
+DNA_DL		6001			# DEC DNA Dump/Load
+DNA_RC		6002			# DEC DNA Remote Console
+DNA_RT		6003			# DEC DNA Routing
+LAT		6004			# DEC LAT
+DIAG		6005			# DEC Diagnostics
+CUST		6006			# DEC Customer use
+SCA		6007			# DEC Systems Comms Arch
+TEB		6558			# Trans Ether Bridging   [RFC1701]
+RAW_FR  	6559			# Raw Frame Relay        [RFC1701]
+RARP		8035			# Reverse ARP            [RFC903]
+AARP		80F3			# Appletalk AARP
+ATALK		809B			# Appletalk
+802_1Q		8100	8021q 1q 802.1q	dot1q # 802.1Q Virtual LAN tagged frame
+IPX		8137			# Novell IPX
+NetBEUI		8191			# NetBEUI
+IPv6		86DD	ip6 		# IP version 6
+PPP		880B			# PPP
+ATMMPOA		884C			# MultiProtocol over ATM
+PPP_DISC	8863			# PPPoE discovery messages
+PPP_SES		8864			# PPPoE session messages
+ATMFATE		8884			# Frame-based ATM Transport over Ethernet
+LOOP		9000	loopback 	# loop proto
diff --git a/userspace/ebtables2/examples/perf_test/perf_test b/userspace/ebtables2/examples/perf_test/perf_test
new file mode 100755
index 0000000..afda266
--- /dev/null
+++ b/userspace/ebtables2/examples/perf_test/perf_test
@@ -0,0 +1,261 @@
+#!/bin/bash
+#
+# The ebtablesu/ebtablesd programs are deprecated,
+# they're mostly useful for debugging ebtables in daemon
+# mode. For fast population of tables, use ebtables-restore.
+# You should probably just ignore this script.
+#
+# This script can be used to compare the speed of
+# the different methods for adding rules in ebtables
+# chains.
+#
+# Apart from the standard method of adding rules with
+# the ebtables tool, rules can be added (faster) with
+# ebtablesd running in the background and accepting
+# commands through a pipe. The pipe can be written to
+# with the standard shell tools. The only restriction is
+# that spaces are only used to separate options and
+# commands, i.e. spaces are not allowed in strings, even
+# if they are between "". E.g.
+# ebtablesu -A --log-prefix "a space"
+# is not allowed, however
+# ebtablesu -A --log-prefix "a_space"
+# is allowed.
+#
+# Author: Bart De Schuymer
+#
+
+export PIPE=/tmp/ebtables-v2.0.7/ebtablesd_pipe
+export EBTABLES=/usr/local/sbin/ebtables
+export EBTABLESD=/usr/local/sbin/ebtablesd
+export EBTABLESU=/usr/local/sbin/ebtablesu
+
+if [[ $# = 0 ]]
+then
+
+rm -f iets niets iets.out niets.out
+MAXLIMIT=10000
+for ((LIMIT=10; LIMIT <= MAXLIMIT; LIMIT *= 10)) do
+  killall ebtablesd 2>/dev/null
+  $EBTABLES --init-table
+  export LIMIT
+  time $0 1
+  echo "added" $LIMIT "rules with echo"
+  $EBTABLES --atomic-file iets --atomic-save
+  $EBTABLES -F
+  time $0 2
+  echo "added" $LIMIT "rules with ebtables"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file iets -L > iets.out
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  killall ebtablesd 2>/dev/null
+  $EBTABLES -F
+  time $0 3
+  echo "added" $LIMIT "rules with ebtablesu"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file iets -L > iets.out
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  time $0 4
+  echo "added" $LIMIT "rules with atomic-file"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  time $0 5
+  echo "added" $LIMIT "rules with one atomic-commit"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f iets iets.out niets niets.out
+done
+
+elif [[ $1 = "1" ]]
+then
+
+$EBTABLESD &
+pid=`jobs -p '$EBTABLESD'`
+sleep 1
+$EBTABLESU open filter
+# Add rules with ebtablesd
+echo "$EBTABLESU -F" >>$PIPE
+for ((a=1; a <= LIMIT; a++)) do
+  echo "$EBTABLESU -A FORWARD" >>$PIPE
+done
+$EBTABLESU commit filter
+$EBTABLESU quit
+wait $pid
+
+elif [[ $1 = "2" ]]
+then
+
+# Add rules with ebtables
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLES -A FORWARD
+done
+
+elif [[ $1 = "3" ]]
+then
+
+$EBTABLESD &
+pid=`jobs -p '$EBTABLESD'`
+sleep 1
+$EBTABLESU open filter
+# Add rules with ebtablesu
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLESU -A FORWARD
+done
+$EBTABLESU commit filter
+$EBTABLESU quit
+wait $pid
+
+elif [[ $1 = "4" ]]
+then
+
+# Add rules with ebtables --atomic-file
+$EBTABLES --atomic-file niets --atomic-init
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLES --atomic-file niets -A FORWARD
+done
+$EBTABLES --atomic-file niets --atomic-commit
+
+else
+
+# Add rules taken from a binary file containing a saved table
+$EBTABLES --atomic-file iets --atomic-commit
+
+fi
+
+
+# From the results below, we can conclude the following about
+# table constructions when speed is an issue.
+# For first-time fast construction of tables, it's best to use the
+#   echo + ebtablesd method. If the echo method is unwanted, it
+#   is best to use the ebtablesu + ebtablesd method.
+#   The echo method is much faster because echo is a bash
+#   built-in command.
+#   Perhaps intialize the kernel tables to empty chains with
+#   policy DROP before constructing the table in userspace.
+# For construction of tables that were constructed earlier, it
+#   is best to save those constructed tables to a binary file
+#   and then use --atomic-commit to get the table in the kernel
+#   (this is lightning fast compared with the other methods).
+#
+# System specs:
+# processor       : 0
+# vendor_id       : AuthenticAMD
+# cpu family      : 6
+# model           : 4
+# model name      : AMD Athlon(tm) processor
+# stepping        : 4
+# cpu MHz         : 1000.592
+# cache size      : 256 KB
+# MemTotal        : 515780 kB
+#
+####10 rules####
+#
+# real    0m0.078s
+# user    0m0.030s
+# sys     0m0.045s
+# added 10 rules with echo
+#
+# real    0m0.098s
+# user    0m0.031s
+# sys     0m0.063s
+# added 10 rules with ebtables
+#
+# real    0m0.275s
+# user    0m0.083s
+# sys     0m0.188s
+# added 10 rules with ebtablesu
+#
+# real    0m0.279s
+# user    0m0.082s
+# sys     0m0.192s
+# added 10 rules with atomic-file
+#
+# real    0m0.017s
+# user    0m0.009s
+# sys     0m0.008s
+# added 10 rules with one atomic-commit
+#
+# real    0m0.095s
+# user    0m0.050s
+# sys     0m0.043s
+# added 100 rules with echo
+#
+# real    0m0.936s
+# user    0m0.315s
+# sys     0m0.587s
+# added 100 rules with ebtables
+#
+# real    0m1.967s
+# user    0m0.449s
+# sys     0m1.479s
+# added 100 rules with ebtablesu
+#
+# real    0m2.472s
+# user    0m0.745s
+# sys     0m1.660s
+# added 100 rules with atomic-file
+#
+# real    0m0.018s
+# user    0m0.006s
+# sys     0m0.012s
+# added 100 rules with one atomic-commit
+#
+# real    0m0.740s
+# user    0m0.461s
+# sys     0m0.265s
+# added 1000 rules with echo
+#
+# real    0m12.471s
+# user    0m4.423s
+# sys     0m7.828s
+# added 1000 rules with ebtables
+#
+# real    0m17.715s
+# user    0m6.814s
+# sys     0m10.604s
+# added 1000 rules with ebtablesu
+#
+# real    0m28.176s
+# user    0m8.875s
+# sys     0m18.704s
+# added 1000 rules with atomic-file
+#
+# real    0m0.025s
+# user    0m0.015s
+# sys     0m0.010s
+# added 1000 rules with one atomic-commit
+#
+# real    1m11.474s
+# user    1m9.662s
+# sys     0m1.035s
+# added 10000 rules with echo
+#
+# real    10m9.418s
+# user    4m19.163s
+# sys     5m37.548s
+# added 10000 rules with ebtables
+#
+# real    2m32.119s
+# user    1m38.085s
+# sys     0m52.298s
+# added 10000 rules with ebtablesu
+#
+# real    13m23.396s
+# user    5m29.770s
+# sys     6m44.598s
+# added 10000 rules with atomic-file
+#
+# real    0m0.319s
+# user    0m0.037s
+# sys     0m0.057s
+# added 10000 rules with one atomic-commit
diff --git a/userspace/ebtables2/examples/ulog/test_ulog.c b/userspace/ebtables2/examples/ulog/test_ulog.c
new file mode 100644
index 0000000..fbbd909
--- /dev/null
+++ b/userspace/ebtables2/examples/ulog/test_ulog.c
@@ -0,0 +1,292 @@
+
+/*
+ * Simple example program to log packets received with the ulog
+ * watcher of ebtables.
+ *
+ * usage:
+ * Add the appropriate ebtables ulog rule, e.g. (0 < NLGROUP < 33):
+ *   ebtables -A FORWARD --ulog-nlgroup NLGROUP
+ * Start this application somewhere:
+ *   test_ulog NLGROUP
+ *
+ * compile with make test_ulog KERNEL_INCLUDES=<path_to_kernel_include_dir>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <time.h>
+#include <linux/netlink.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <netinet/if_ether.h>
+#include <netinet/ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include "../../include/ebtables_u.h"
+#include "../../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_ulog.h>
+
+/* <linux/if_vlan.h> doesn't hand this to userspace :-( */
+#define VLAN_HLEN 4
+struct vlan_hdr {
+	unsigned short TCI;
+	unsigned short encap;
+};
+
+static struct sockaddr_nl sa_local =
+{
+	.nl_family = AF_NETLINK,
+	.nl_groups = 1,
+};
+
+static struct sockaddr_nl sa_kernel =
+{
+	.nl_family = AF_NETLINK,
+	.nl_pid = 0,
+	.nl_groups = 1,
+};
+
+static const char *hookstr[NF_BR_NUMHOOKS] =
+{
+        [NF_BR_POST_ROUTING] "POSTROUTING",
+         [NF_BR_PRE_ROUTING] "PREROUTING",
+           [NF_BR_LOCAL_OUT] "OUTPUT",
+            [NF_BR_LOCAL_IN] "INPUT",
+            [NF_BR_BROUTING] "BROUTING",
+             [NF_BR_FORWARD] "FORWARD"
+};
+
+#define DEBUG_QUEUE 0
+#define BUFLEN 65536
+static char buf[BUFLEN];
+static int sfd;
+
+/* Get the next ebt_ulog packet, talk to the kernel if necessary */
+ebt_ulog_packet_msg_t *ulog_get_packet()
+{
+	static struct nlmsghdr *nlh = NULL;
+	static int len, remain_len;
+	static int pkts_per_msg = 0;
+	ebt_ulog_packet_msg_t *msg;
+	socklen_t addrlen = sizeof(sa_kernel);
+
+	if (!nlh) {
+recv_new:
+		if (pkts_per_msg && DEBUG_QUEUE)
+			printf("PACKETS IN LAST MSG: %d\n", pkts_per_msg);
+		pkts_per_msg = 0;
+		len = recvfrom(sfd, buf, BUFLEN, 0,
+		               (struct sockaddr *)&sa_kernel, &addrlen);
+		if (addrlen != sizeof(sa_kernel)) {
+			printf("addrlen %u != %u\n", addrlen,
+			       (uint32_t)sizeof(sa_kernel));
+			exit(-1);
+		}
+		if (len == -1) {
+			perror("recvmsg");
+			if (errno == EINTR)
+				goto recv_new;
+			exit(-1);
+		}
+		nlh = (struct nlmsghdr *)buf;
+		if (nlh->nlmsg_flags & MSG_TRUNC || len > BUFLEN) {
+			printf("Packet truncated");
+			exit(-1);
+		}
+		if (!NLMSG_OK(nlh, BUFLEN)) {
+			perror("Netlink message parse error\n");
+			return NULL;
+		}
+	}
+
+	msg = NLMSG_DATA(nlh);
+
+	remain_len = (len - ((char *)nlh - buf));
+	if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_type != NLMSG_DONE)
+		nlh = NLMSG_NEXT(nlh, remain_len);
+	else
+		nlh = NULL;
+
+	pkts_per_msg++;
+	return msg;
+}
+
+int main(int argc, char **argv)
+{
+	int i, curr_len, pktcnt = 0;
+	int rcvbufsize = BUFLEN;
+	ebt_ulog_packet_msg_t *msg;
+	struct ethhdr *ehdr;
+	struct ethertypeent *etype;
+	struct protoent *prototype;
+	struct iphdr *iph;
+	struct icmphdr *icmph;
+	struct tm* ptm;
+	char time_str[40], *ctmp;
+
+	if (argc == 2) {
+		i = strtoul(argv[1], &ctmp, 10);
+		if (*ctmp != '\0' || i < 1 || i > 32) {
+			printf("Usage: %s <group number>\nWith  0 < group "
+			       "number < 33\n", argv[0]);
+			exit(0);
+		}
+		sa_local.nl_groups = sa_kernel.nl_groups = 1 << (i - 1);
+	}
+
+	sa_local.nl_pid = getpid();
+	sfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
+	if (!sfd) {
+		perror("socket");
+		exit(-1);
+	}
+
+	if (bind(sfd, (struct sockaddr *)(&sa_local), sizeof(sa_local)) ==
+	    -1) {
+		perror("bind");
+		exit(-1);
+	}
+	i = setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+	               sizeof(rcvbufsize));
+
+	while (1) {
+		if (!(msg = ulog_get_packet()))
+			continue;
+
+		if (msg->version != EBT_ULOG_VERSION) {
+			printf("WRONG VERSION: %d INSTEAD OF %d\n",
+			       msg->version, EBT_ULOG_VERSION);
+			continue;
+		}
+		printf("PACKET NR: %d\n", ++pktcnt);
+		iph = NULL;
+		curr_len = ETH_HLEN;
+
+		printf("INDEV=%s\nOUTDEV=%s\nPHYSINDEV=%s\nPHYSOUTDEV=%s\n"
+		       "PREFIX='%s'", msg->indev, msg->outdev, msg->physindev,
+		       msg->physoutdev, msg->prefix);
+
+		ptm = localtime(&msg->stamp.tv_sec);
+		strftime (time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", ptm);
+		printf("\nARRIVAL TIME: %s\nMARK=%lu\nHOOK=%s\n", time_str,
+		       msg->mark, hookstr[msg->hook]);
+
+		if (msg->data_len < curr_len) {
+			printf("====>Packet smaller than Ethernet header "
+			       "length<====\n");
+			goto letscontinue;
+		}
+
+		printf("::::::::ETHERNET:HEADER::::::::\n");
+
+		ehdr = (struct ethhdr *)msg->data;
+		printf("MAC SRC=%s\n", ether_ntoa((const struct ether_addr *)
+		       ehdr->h_source));
+		printf("MAC DST=%s\nETHERNET PROTOCOL=", ether_ntoa(
+		       (const struct ether_addr *)ehdr->h_dest));
+		etype = getethertypebynumber(ntohs(ehdr->h_proto));
+		if (!etype)
+			printf("0x%x\n", ntohs(ehdr->h_proto));
+		else
+			printf("%s\n", etype->e_name);
+
+		if (ehdr->h_proto == htons(ETH_P_8021Q)) {
+			struct vlan_hdr *vlanh = (struct vlan_hdr *)
+			                          (((char *)ehdr) + curr_len);
+			printf("::::::::::VLAN:HEADER::::::::::\n");
+			curr_len += VLAN_HLEN;
+			if (msg->data_len < curr_len) {
+				printf("====>Packet only contains partial "
+				       "VLAN header<====\n");
+				goto letscontinue;
+			}
+
+			printf("VLAN TCI=%d\n", ntohs(vlanh->TCI));
+			printf("VLAN ENCAPS PROTOCOL=");
+			etype = getethertypebynumber(ntohs(vlanh->encap));
+			if (!etype)
+				printf("0x%x\n", ntohs(vlanh->encap));
+			else
+				printf("%s\n", etype->e_name);
+			if (ehdr->h_proto == htons(ETH_P_IP))
+				iph = (struct iphdr *)(vlanh + 1);
+		} else if (ehdr->h_proto == htons(ETH_P_IP))
+			iph = (struct iphdr *)(((char *)ehdr) + curr_len);
+		if (!iph)
+			goto letscontinue;
+
+		curr_len += sizeof(struct iphdr);
+		if (msg->data_len < curr_len || msg->data_len <
+		    (curr_len += iph->ihl * 4 - sizeof(struct iphdr))) {
+			printf("====>Packet only contains partial IP "
+			       "header<====\n");
+			goto letscontinue;
+		}
+
+		printf(":::::::::::IP:HEADER:::::::::::\n");
+		printf("IP SRC ADDR=");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&iph->saddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("\nIP DEST ADDR=");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&iph->daddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("\nIP PROTOCOL=");
+		if (!(prototype = getprotobynumber(iph->protocol)))
+			printf("%d\n", iph->protocol);
+		else
+			printf("%s\n", prototype->p_name);
+
+		if (iph->protocol != IPPROTO_ICMP)
+			goto letscontinue;
+
+		icmph = (struct icmphdr *)(((char *)ehdr) + curr_len);
+		curr_len += 4;
+		if (msg->data_len < curr_len) {
+truncated_icmp:
+			printf("====>Packet only contains partial ICMP "
+			       "header<====\n");
+			goto letscontinue;
+		}
+		if (icmph->type != ICMP_ECHO && icmph->type != ICMP_ECHOREPLY)
+			goto letscontinue;
+
+		curr_len += 4;
+		if (msg->data_len < curr_len)
+			goto truncated_icmp;
+		/* Normally the process id, it's sent out in machine
+		 * byte order */
+
+		printf("ICMP_ECHO IDENTIFIER=%u\n", icmph->un.echo.id);
+		printf("ICMP_ECHO SEQ NR=%u\n", ntohs(icmph->un.echo.sequence));
+
+letscontinue:
+		printf("===>Total Packet length: %ld, of which we examined "
+		       "%d bytes\n", msg->data_len, curr_len);
+		printf("###############################\n"
+		       "######END#OF##PACKET#DUMP######\n"
+		       "###############################\n");
+	}
+
+	return 0;
+}
diff --git a/userspace/ebtables2/extensions/Makefile b/userspace/ebtables2/extensions/Makefile
new file mode 100644
index 0000000..b3548e8
--- /dev/null
+++ b/userspace/ebtables2/extensions/Makefile
@@ -0,0 +1,30 @@
+#! /usr/bin/make
+
+EXT_FUNC+=802_3 nat arp arpreply ip ip6 standard log redirect vlan mark_m mark \
+          pkttype stp among limit ulog nflog
+EXT_TABLES+=filter nat broute
+EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+EXT_LIBS+=$(foreach T,$(EXT_FUNC), extensions/libebt_$(T).so)
+EXT_LIBS+=$(foreach T,$(EXT_TABLES), extensions/libebtable_$(T).so)
+EXT_LIBSI+=$(foreach T,$(EXT_FUNC), -lebt_$(T))
+EXT_LIBSI+=$(foreach T,$(EXT_TABLES), -lebtable_$(T))
+
+extensions/ebt_%.so: extensions/ebt_%.o
+	$(CC) $(LDFLAGS) -shared -o $@ -lc $< -nostartfiles
+
+extensions/libebt_%.so: extensions/ebt_%.so
+	mv $< $@
+
+extensions/ebtable_%.so: extensions/ebtable_%.o
+	$(CC) $(LDFLAGS) -shared -o $@ -lc $< -nostartfiles
+
+extensions/libebtable_%.so: extensions/ebtable_%.so
+	mv $< $@
+
+extensions/ebt_%.o: extensions/ebt_%.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
+extensions/ebtable_%.o: extensions/ebtable_%.c
+	$(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+
diff --git a/userspace/ebtables2/extensions/ebt_802_3.c b/userspace/ebtables2/extensions/ebt_802_3.c
new file mode 100644
index 0000000..dd22eb2
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_802_3.c
@@ -0,0 +1,147 @@
+/* 802_3
+ *
+ * Author:
+ * Chris Vitale <csv@bluetail.com>
+ *
+ * May 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_802_3.h>
+
+#define _802_3_SAP '1'
+#define _802_3_TYPE '2'
+
+static struct option opts[] =
+{
+	{ "802_3-sap"   , required_argument, 0, _802_3_SAP },
+	{ "802_3-type"  , required_argument, 0, _802_3_TYPE },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"802_3 options:\n"
+"--802_3-sap [!] protocol       : 802.3 DSAP/SSAP- 1 byte value (hex)\n"
+"  DSAP and SSAP are always the same.  One SAP applies to both fields\n"
+"--802_3-type [!] protocol      : 802.3 SNAP Type- 2 byte value (hex)\n"
+"  Type implies SAP value 0xaa\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+	info->invflags = 0;
+	info->bitmask = 0;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data;
+	unsigned int i;
+	char *end;
+
+	switch (c) {
+		case _802_3_SAP:
+			ebt_check_option2(flags, _802_3_SAP);
+			if (ebt_check_inverse2(optarg))
+				info->invflags |= EBT_802_3_SAP;
+			i = strtoul(optarg, &end, 16);
+			if (i > 255 || *end != '\0') 
+				ebt_print_error2("Problem with specified "
+						"sap hex value, %x",i);
+			info->sap = i; /* one byte, so no byte order worries */
+			info->bitmask |= EBT_802_3_SAP;
+			break;
+		case _802_3_TYPE:
+			ebt_check_option2(flags, _802_3_TYPE);
+			if (ebt_check_inverse2(optarg))
+				info->invflags |= EBT_802_3_TYPE;
+			i = strtoul(optarg, &end, 16);
+			if (i > 65535 || *end != '\0') {
+				ebt_print_error2("Problem with the specified "
+						"type hex value, %x",i);
+			}
+			info->type = htons(i);
+			info->bitmask |= EBT_802_3_TYPE;
+			break;
+		default:
+			return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	if (!(entry->bitmask & EBT_802_3))
+		ebt_print_error("For 802.3 DSAP/SSAP filtering the protocol "
+				"must be LENGTH");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+	if (info->bitmask & EBT_802_3_SAP) {
+		printf("--802_3-sap ");
+		if (info->invflags & EBT_802_3_SAP)
+			printf("! ");
+		printf("0x%.2x ", info->sap);
+	}
+	if (info->bitmask & EBT_802_3_TYPE) {
+		printf("--802_3-type ");
+		if (info->invflags & EBT_802_3_TYPE)
+			printf("! ");
+		printf("0x%.4x ", ntohs(info->type));
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_802_3_info *info1 = (struct ebt_802_3_info *)m1->data;
+	struct ebt_802_3_info *info2 = (struct ebt_802_3_info *)m2->data;
+
+	if (info1->bitmask != info2->bitmask)
+		return 0;
+	if (info1->invflags != info2->invflags)
+		return 0;
+	if (info1->bitmask & EBT_802_3_SAP) {
+		if (info1->sap != info2->sap) 
+			return 0;
+	}
+	if (info1->bitmask & EBT_802_3_TYPE) {
+		if (info1->type != info2->type)
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match _802_3_match = 
+{
+	.name		= "802_3",
+	.size		= sizeof(struct ebt_802_3_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&_802_3_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_among.c b/userspace/ebtables2/extensions/ebt_among.c
new file mode 100644
index 0000000..f97d07e
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_among.c
@@ -0,0 +1,497 @@
+/* ebt_among
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include "../include/ethernetdb.h"
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define AMONG_DST '1'
+#define AMONG_SRC '2'
+#define AMONG_DST_F '3'
+#define AMONG_SRC_F '4'
+
+static struct option opts[] = {
+	{"among-dst", required_argument, 0, AMONG_DST},
+	{"among-src", required_argument, 0, AMONG_SRC},
+	{"among-dst-file", required_argument, 0, AMONG_DST_F},
+	{"among-src-file", required_argument, 0, AMONG_SRC_F},
+	{0}
+};
+
+#ifdef DEBUG
+static void hexdump(const void *mem, int howmany)
+{
+	printf("\n");
+	const unsigned char *p = mem;
+	int i;
+
+	for (i = 0; i < howmany; i++) {
+		if (i % 32 == 0) {
+			printf("\n%04x: ", i);
+		}
+		printf("%2.2x%c", p[i], ". "[i % 4 == 3]);
+	}
+	printf("\n");
+}
+#endif				/* DEBUG */
+
+static void print_help()
+{
+	printf(
+"`among' options:\n"
+"--among-dst      [!] list      : matches if ether dst is in list\n"
+"--among-src      [!] list      : matches if ether src is in list\n"
+"--among-dst-file [!] file      : obtain dst list from file\n"
+"--among-src-file [!] file      : obtain src list from file\n"
+"list has form:\n"
+" xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]"
+",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n"
+"Things in brackets are optional.\n"
+"If you want to allow two (or more) IP addresses to one MAC address, you\n"
+"can specify two (or more) pairs with the same MAC, e.g.\n"
+" 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n"
+	);
+}
+static int old_size;
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_among_info *amonginfo =
+	    (struct ebt_among_info *) match->data;
+
+	memset(amonginfo, 0, sizeof(struct ebt_among_info));
+	old_size = sizeof(struct ebt_among_info);
+}
+
+static struct ebt_mac_wormhash *new_wormhash(int n)
+{
+	int size =
+	    sizeof(struct ebt_mac_wormhash) +
+	    n * sizeof(struct ebt_mac_wormhash_tuple);
+	struct ebt_mac_wormhash *result =
+	    (struct ebt_mac_wormhash *) malloc(size);
+
+	if (!result)
+		ebt_print_memory();
+	memset(result, 0, size);
+	result->poolsize = n;
+	return result;
+}
+
+static void copy_wormhash(struct ebt_mac_wormhash *d,
+			  const struct ebt_mac_wormhash *s)
+{
+	int dpoolsize = d->poolsize;
+	int dsize, ssize, amount;
+
+	dsize = ebt_mac_wormhash_size(d);
+	ssize = ebt_mac_wormhash_size(s);
+	amount = dsize < ssize ? dsize : ssize;
+	memcpy(d, s, amount);
+	d->poolsize = dpoolsize;
+}
+
+/* Returns:
+ * -1 when '\0' reached
+ * -2 when `n' bytes read and no delimiter found
+ *  0 when no less than `n' bytes read and delimiter found
+ * if `destbuf' is not NULL, it is filled by read bytes and ended with '\0'
+ * *pp is set on the first byte not copied to `destbuf'
+ */
+static int read_until(const char **pp, const char *delimiters,
+		      char *destbuf, int n)
+{
+	int count = 0;
+	int ret = 0;
+	char c;
+
+	while (1) {
+		c = **pp;
+		if (!c) {
+			ret = -1;
+			break;
+		}
+		if (strchr(delimiters, c)) {
+			ret = 0;
+			break;
+		}
+		if (count == n) {
+			ret = -2;
+			break;
+		}
+		if (destbuf)
+			destbuf[count++] = c;
+		(*pp)++;
+	}
+	if (destbuf)
+		destbuf[count] = 0;
+	return ret;
+}
+
+static int fcmp(const void *va, const void *vb) {
+	const struct ebt_mac_wormhash_tuple *a = va;
+	const struct ebt_mac_wormhash_tuple *b = vb;
+	int ca = ((const unsigned char*)a->cmp)[7];
+	int cb = ((const unsigned char*)b->cmp)[7];
+
+	return ca - cb;
+}
+
+static void index_table(struct ebt_mac_wormhash *wh)
+{
+	int ipool, itable;
+	int c;
+
+	for (itable = 0; itable <= 256; itable++) {
+		wh->table[itable] = wh->poolsize;
+	}
+	ipool = 0;
+	itable = 0;
+	while (1) {
+		wh->table[itable] = ipool;
+		c = ((const unsigned char*)wh->pool[ipool].cmp)[7];
+		if (itable <= c) {
+			itable++;
+		} else {
+			ipool++;
+		}
+		if (ipool > wh->poolsize)
+			break;
+	}
+}
+
+static struct ebt_mac_wormhash *create_wormhash(const char *arg)
+{
+	const char *pc = arg;
+	const char *anchor;
+	char *endptr;
+	struct ebt_mac_wormhash *workcopy, *result, *h;
+	unsigned char mac[6];
+	unsigned char ip[4];
+	int nmacs = 0;
+	int i;
+	char token[4];
+
+	if (!(workcopy = new_wormhash(1024))) {
+		ebt_print_memory();
+	}
+	while (1) {
+		/* remember current position, we'll need it on error */
+		anchor = pc;
+
+		/* collect MAC; all its bytes are followed by ':' (colon),
+		 * except for the last one which can be followed by 
+		 * ',' (comma), '=' or '\0' */
+		for (i = 0; i < 5; i++) {
+			if (read_until(&pc, ":", token, 2) < 0
+			    || token[0] == 0) {
+				ebt_print_error("MAC parse error: %.20s", anchor);
+				free(workcopy);
+				return NULL;
+			}
+			mac[i] = strtol(token, &endptr, 16);
+			if (*endptr) {
+				ebt_print_error("MAC parse error: %.20s", anchor);
+				free(workcopy);
+				return NULL;
+			}
+			pc++;
+		}
+		if (read_until(&pc, "=,", token, 2) == -2 || token[0] == 0) {
+			ebt_print_error("MAC parse error: %.20s", anchor);
+			return NULL;
+		}
+		mac[i] = strtol(token, &endptr, 16);
+		if (*endptr) {
+			ebt_print_error("MAC parse error: %.20s", anchor);
+			return NULL;
+		}
+		if (*pc == '=') {
+			/* an IP follows the MAC; collect similarly to MAC */
+			pc++;
+			anchor = pc;
+			for (i = 0; i < 3; i++) {
+				if (read_until(&pc, ".", token, 3) < 0 || token[0] == 0) {
+					ebt_print_error("IP parse error: %.20s", anchor);
+					return NULL;
+				}
+				ip[i] = strtol(token, &endptr, 10);
+				if (*endptr) {
+					ebt_print_error("IP parse error: %.20s", anchor);
+					return NULL;
+				}
+				pc++;
+			}
+			if (read_until(&pc, ",", token, 3) == -2 || token[0] == 0) {
+				ebt_print_error("IP parse error: %.20s", anchor);
+				return NULL;
+			}
+			ip[3] = strtol(token, &endptr, 10);
+			if (*endptr) {
+				ebt_print_error("IP parse error: %.20s", anchor);
+				return NULL;
+			}
+			if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
+				ebt_print_error("Illegal IP 0.0.0.0");
+				return NULL;
+			}
+		} else {
+			/* no IP, we set it to 0.0.0.0 */
+			memset(ip, 0, 4);
+		}
+
+		/* we have collected MAC and IP, so we add an entry */
+		memcpy(((char *) workcopy->pool[nmacs].cmp) + 2, mac, 6);
+		memcpy(&(workcopy->pool[nmacs].ip), ip, 4);
+		nmacs++;
+
+		/* re-allocate memory if needed */
+		if (*pc && nmacs >= workcopy->poolsize) {
+			if (!(h = new_wormhash(nmacs * 2))) {
+				ebt_print_memory();
+			}
+			copy_wormhash(h, workcopy);
+			free(workcopy);
+			workcopy = h;
+		}
+
+		/* check if end of string was reached */
+		if (!*pc) {
+			break;
+		}
+
+		/* now `pc' points to comma if we are here; */
+		/* increment this to the next char */
+		/* but first assert :-> */
+		if (*pc != ',') {
+			ebt_print_error("Something went wrong; no comma...\n");
+			return NULL;
+		}
+		pc++;
+
+		/* again check if end of string was reached; */
+		/* we allow an ending comma */
+		if (!*pc) {
+			break;
+		}
+	}
+	if (!(result = new_wormhash(nmacs))) {
+		ebt_print_memory();
+	}
+	copy_wormhash(result, workcopy);
+	free(workcopy);
+	qsort(&result->pool, result->poolsize,
+			sizeof(struct ebt_mac_wormhash_tuple), fcmp);
+	index_table(result);
+	return result;
+}
+
+#define OPT_DST 0x01
+#define OPT_SRC 0x02
+static int parse(int c, char **argv, int argc,
+		 const struct ebt_u_entry *entry, unsigned int *flags,
+		 struct ebt_entry_match **match)
+{
+	struct ebt_among_info *info =
+	    (struct ebt_among_info *) (*match)->data;
+	struct ebt_mac_wormhash *wh;
+	struct ebt_entry_match *h;
+	int new_size;
+	long flen = 0;
+	int fd = -1;
+
+	switch (c) {
+	case AMONG_DST_F:
+	case AMONG_SRC_F:
+	case AMONG_DST:
+	case AMONG_SRC:
+		if (c == AMONG_DST || c == AMONG_DST_F) {
+			ebt_check_option2(flags, OPT_DST);
+		} else {
+			ebt_check_option2(flags, OPT_SRC);
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == AMONG_DST || c == AMONG_DST_F)
+				info->bitmask |= EBT_AMONG_DST_NEG;
+			else
+				info->bitmask |= EBT_AMONG_SRC_NEG;
+		}
+		if (c == AMONG_DST_F || c == AMONG_SRC_F) {
+			struct stat stats;
+
+			if ((fd = open(optarg, O_RDONLY)) == -1)
+				ebt_print_error("Couldn't open file '%s'", optarg);
+			fstat(fd, &stats);
+			flen = stats.st_size;
+			/* use mmap because the file will probably be big */
+			optarg = mmap(0, flen, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+			if (optarg == MAP_FAILED)
+				ebt_print_error("Couldn't map file to memory");
+			if (optarg[flen-1] != '\n')
+				ebt_print_error("File should end with a newline");
+			if (strchr(optarg, '\n') != optarg+flen-1)
+				ebt_print_error("File should only contain one line");
+			optarg[flen-1] = '\0';
+			if (ebt_errormsg[0] != '\0') {
+				munmap(argv, flen);
+				close(fd);
+				exit(-1);
+			}
+		}
+		wh = create_wormhash(optarg);
+		if (ebt_errormsg[0] != '\0')
+			break;
+
+		new_size = old_size+ebt_mac_wormhash_size(wh);
+		h = malloc(sizeof(struct ebt_entry_match)+EBT_ALIGN(new_size));
+		if (!h)
+			ebt_print_memory();
+		memcpy(h, *match, old_size+sizeof(struct ebt_entry_match));
+		memcpy((char *)h+old_size+sizeof(struct ebt_entry_match), wh,
+		       ebt_mac_wormhash_size(wh));
+		h->match_size = EBT_ALIGN(new_size);
+		info = (struct ebt_among_info *) h->data;
+		if (c == AMONG_DST || c == AMONG_DST_F) {
+			info->wh_dst_ofs = old_size;
+		} else {
+			info->wh_src_ofs = old_size;
+		}
+		old_size = new_size;
+		free(*match);
+		*match = h;
+		free(wh);
+		if (c == AMONG_DST_F || c == AMONG_SRC_F) {
+			munmap(argv, flen);
+			close(fd);
+		}
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+			const struct ebt_entry_match *match,
+			const char *name, unsigned int hookmask,
+			unsigned int time)
+{
+}
+
+#ifdef DEBUG
+static void wormhash_debug(const struct ebt_mac_wormhash *wh)
+{
+	int i;
+
+	printf("poolsize: %d\n", wh->poolsize);
+	for (i = 0; i <= 256; i++) {
+		printf("%02x ", wh->table[i]);
+		if (i % 16 == 15) {
+			printf("\n");
+		}
+	}
+	printf("\n");
+}
+#endif /* DEBUG */
+
+static void wormhash_printout(const struct ebt_mac_wormhash *wh)
+{
+	int i;
+	unsigned char *ip;
+
+	for (i = 0; i < wh->poolsize; i++) {
+		const struct ebt_mac_wormhash_tuple *p;
+
+		p = (const struct ebt_mac_wormhash_tuple *)(&wh->pool[i]);
+		ebt_print_mac(((const unsigned char *) &p->cmp[0]) + 2);
+		if (p->ip) {
+			ip = (unsigned char *) &p->ip;
+			printf("=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+		}
+		printf(",");
+	}
+	printf(" ");
+}
+
+static void print(const struct ebt_u_entry *entry,
+		  const struct ebt_entry_match *match)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *)match->data;
+
+	if (info->wh_dst_ofs) {
+		printf("--among-dst ");
+		if (info->bitmask && EBT_AMONG_DST_NEG) {
+			printf("! ");
+		}
+		wormhash_printout(ebt_among_wh_dst(info));
+	}
+	if (info->wh_src_ofs) {
+		printf("--among-src ");
+		if (info->bitmask && EBT_AMONG_SRC_NEG) {
+			printf("! ");
+		}
+		wormhash_printout(ebt_among_wh_src(info));
+	}
+}
+
+static int compare_wh(const struct ebt_mac_wormhash *aw,
+		      const struct ebt_mac_wormhash *bw)
+{
+	int as, bs;
+
+	as = ebt_mac_wormhash_size(aw);
+	bs = ebt_mac_wormhash_size(bw);
+	if (as != bs)
+		return 0;
+	if (as && memcmp(aw, bw, as))
+		return 0;
+	return 1;
+}
+
+static int compare(const struct ebt_entry_match *m1,
+		   const struct ebt_entry_match *m2)
+{
+	struct ebt_among_info *a = (struct ebt_among_info *) m1->data;
+	struct ebt_among_info *b = (struct ebt_among_info *) m2->data;
+
+	if (!compare_wh(ebt_among_wh_dst(a), ebt_among_wh_dst(b)))
+		return 0;
+	if (!compare_wh(ebt_among_wh_src(a), ebt_among_wh_src(b)))
+		return 0;
+	if (a->bitmask != b->bitmask)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match among_match = {
+	.name 		= "among",
+	.size 		= sizeof(struct ebt_among_info),
+	.help 		= print_help,
+	.init 		= init,
+	.parse 		= parse,
+	.final_check 	= final_check,
+	.print 		= print,
+	.compare 	= compare,
+	.extra_ops 	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&among_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_arp.c b/userspace/ebtables2/extensions/ebt_arp.c
new file mode 100644
index 0000000..64d337d
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_arp.c
@@ -0,0 +1,368 @@
+/* ebt_arp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Tim Gardner <timg@tpi.com>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+
+#define ARP_OPCODE '1'
+#define ARP_HTYPE  '2'
+#define ARP_PTYPE  '3'
+#define ARP_IP_S   '4'
+#define ARP_IP_D   '5'
+#define ARP_MAC_S  '6'
+#define ARP_MAC_D  '7'
+#define ARP_GRAT   '8'
+static struct option opts[] =
+{
+	{ "arp-opcode"    , required_argument, 0, ARP_OPCODE },
+	{ "arp-op"        , required_argument, 0, ARP_OPCODE },
+	{ "arp-htype"     , required_argument, 0, ARP_HTYPE  },
+	{ "arp-ptype"     , required_argument, 0, ARP_PTYPE  },
+	{ "arp-ip-src"    , required_argument, 0, ARP_IP_S   },
+	{ "arp-ip-dst"    , required_argument, 0, ARP_IP_D   },
+	{ "arp-mac-src"   , required_argument, 0, ARP_MAC_S  },
+	{ "arp-mac-dst"   , required_argument, 0, ARP_MAC_D  },
+	{ "arp-gratuitous",       no_argument, 0, ARP_GRAT   },
+	{ 0 }
+};
+
+#define NUMOPCODES 9
+/* a few names */
+static char *opcodes[] =
+{
+	"Request",
+	"Reply",
+	"Request_Reverse",
+	"Reply_Reverse",
+	"DRARP_Request",
+	"DRARP_Reply",
+	"DRARP_Error",
+	"InARP_Request",
+	"ARP_NAK",
+};
+
+static void print_help()
+{
+	int i;
+
+	printf(
+"arp options:\n"
+"--arp-opcode  [!] opcode        : ARP opcode (integer or string)\n"
+"--arp-htype   [!] type          : ARP hardware type (integer or string)\n"
+"--arp-ptype   [!] type          : ARP protocol type (hexadecimal or string)\n"
+"--arp-ip-src  [!] address[/mask]: ARP IP source specification\n"
+"--arp-ip-dst  [!] address[/mask]: ARP IP target specification\n"
+"--arp-mac-src [!] address[/mask]: ARP MAC source specification\n"
+"--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n"
+"[!] --arp-gratuitous            : ARP gratuitous packet\n"
+" opcode strings: \n");
+	for (i = 0; i < NUMOPCODES; i++)
+		printf(" %d = %s\n", i + 1, opcodes[i]);
+	printf(
+" hardware type string: 1 = Ethernet\n"
+" protocol type string: see "_PATH_ETHERTYPES"\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+
+	arpinfo->invflags = 0;
+	arpinfo->bitmask = 0;
+}
+
+
+#define OPT_OPCODE 0x01
+#define OPT_HTYPE  0x02
+#define OPT_PTYPE  0x04
+#define OPT_IP_S   0x08
+#define OPT_IP_D   0x10
+#define OPT_MAC_S  0x20
+#define OPT_MAC_D  0x40
+#define OPT_GRAT   0x80
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+	long int i;
+	char *end;
+	uint32_t *addr;
+	uint32_t *mask;
+	unsigned char *maddr;
+	unsigned char *mmask;
+
+	switch (c) {
+	case ARP_OPCODE:
+		ebt_check_option2(flags, OPT_OPCODE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_OPCODE;
+		i = strtol(optarg, &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			for (i = 0; i < NUMOPCODES; i++)
+				if (!strcasecmp(opcodes[i], optarg))
+					break;
+			if (i == NUMOPCODES)
+				ebt_print_error2("Problem with specified ARP opcode");
+			i++;
+		}
+		arpinfo->opcode = htons(i);
+		arpinfo->bitmask |= EBT_ARP_OPCODE;
+		break;
+
+	case ARP_HTYPE:
+		ebt_check_option2(flags, OPT_HTYPE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_HTYPE;
+		i = strtol(optarg, &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			if (!strcasecmp("Ethernet", argv[optind - 1]))
+				i = 1;
+			else
+				ebt_print_error2("Problem with specified ARP hardware type");
+		}
+		arpinfo->htype = htons(i);
+		arpinfo->bitmask |= EBT_ARP_HTYPE;
+		break;
+
+	case ARP_PTYPE:
+	{
+		uint16_t proto;
+
+		ebt_check_option2(flags, OPT_PTYPE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_PTYPE;
+
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			struct ethertypeent *ent;
+
+			ent = getethertypebyname(argv[optind - 1]);
+			if (!ent)
+				ebt_print_error2("Problem with specified ARP "
+						"protocol type");
+			proto = ent->e_ethertype;
+
+		} else
+			proto = i;
+		arpinfo->ptype = htons(proto);
+		arpinfo->bitmask |= EBT_ARP_PTYPE;
+		break;
+	}
+
+	case ARP_IP_S:
+	case ARP_IP_D:
+		if (c == ARP_IP_S) {
+			ebt_check_option2(flags, OPT_IP_S);
+			addr = &arpinfo->saddr;
+			mask = &arpinfo->smsk;
+			arpinfo->bitmask |= EBT_ARP_SRC_IP;
+		} else {
+			ebt_check_option2(flags, OPT_IP_D);
+			addr = &arpinfo->daddr;
+			mask = &arpinfo->dmsk;
+			arpinfo->bitmask |= EBT_ARP_DST_IP;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == ARP_IP_S)
+				arpinfo->invflags |= EBT_ARP_SRC_IP;
+			else
+				arpinfo->invflags |= EBT_ARP_DST_IP;
+		}
+		ebt_parse_ip_address(optarg, addr, mask);
+		break;
+
+	case ARP_MAC_S:
+	case ARP_MAC_D:
+		if (c == ARP_MAC_S) {
+			ebt_check_option2(flags, OPT_MAC_S);
+			maddr = arpinfo->smaddr;
+			mmask = arpinfo->smmsk;
+			arpinfo->bitmask |= EBT_ARP_SRC_MAC;
+		} else {
+			ebt_check_option2(flags, OPT_MAC_D);
+			maddr = arpinfo->dmaddr;
+			mmask = arpinfo->dmmsk;
+			arpinfo->bitmask |= EBT_ARP_DST_MAC;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == ARP_MAC_S)
+				arpinfo->invflags |= EBT_ARP_SRC_MAC;
+			else
+				arpinfo->invflags |= EBT_ARP_DST_MAC;
+		}
+		if (ebt_get_mac_and_mask(optarg, maddr, mmask))
+			ebt_print_error2("Problem with ARP MAC address argument");
+		break;
+	case ARP_GRAT:
+		ebt_check_option2(flags, OPT_GRAT);
+		arpinfo->bitmask |= EBT_ARP_GRAT;
+		if (ebt_invert)
+			arpinfo->invflags |= EBT_ARP_GRAT;
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) ||
+	    entry->invflags & EBT_IPROTO)
+		ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+	int i;
+
+	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+		int opcode = ntohs(arpinfo->opcode);
+		printf("--arp-op ");
+		if (arpinfo->invflags & EBT_ARP_OPCODE)
+			printf("! ");
+		if (opcode > 0 && opcode <= NUMOPCODES)
+			printf("%s ", opcodes[opcode - 1]);
+		else
+			printf("%d ", opcode);
+	}
+	if (arpinfo->bitmask & EBT_ARP_HTYPE) {
+		printf("--arp-htype ");
+		if (arpinfo->invflags & EBT_ARP_HTYPE)
+			printf("! ");
+		printf("%d ", ntohs(arpinfo->htype));
+	}
+	if (arpinfo->bitmask & EBT_ARP_PTYPE) {
+		struct ethertypeent *ent;
+
+		printf("--arp-ptype ");
+		if (arpinfo->invflags & EBT_ARP_PTYPE)
+			printf("! ");
+		ent = getethertypebynumber(ntohs(arpinfo->ptype));
+		if (!ent)
+			printf("0x%x ", ntohs(arpinfo->ptype));
+		else
+			printf("%s ", ent->e_name);
+	}
+	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+		printf("--arp-ip-src ");
+		if (arpinfo->invflags & EBT_ARP_SRC_IP)
+			printf("! ");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(arpinfo->smsk));
+	}
+	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+		printf("--arp-ip-dst ");
+		if (arpinfo->invflags & EBT_ARP_DST_IP)
+			printf("! ");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk));
+	}
+	if (arpinfo->bitmask & EBT_ARP_SRC_MAC) {
+		printf("--arp-mac-src ");
+		if (arpinfo->invflags & EBT_ARP_SRC_MAC)
+			printf("! ");
+		ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk);
+		printf(" ");
+	}
+	if (arpinfo->bitmask & EBT_ARP_DST_MAC) {
+		printf("--arp-mac-dst ");
+		if (arpinfo->invflags & EBT_ARP_DST_MAC)
+			printf("! ");
+		ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk);
+		printf(" ");
+	}
+	if (arpinfo->bitmask & EBT_ARP_GRAT) {
+		if (arpinfo->invflags & EBT_ARP_GRAT)
+			printf("! ");
+		printf("--arp-gratuitous ");
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data;
+	struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data;
+
+	if (arpinfo1->bitmask != arpinfo2->bitmask)
+		return 0;
+	if (arpinfo1->invflags != arpinfo2->invflags)
+		return 0;
+	if (arpinfo1->bitmask & EBT_ARP_OPCODE) {
+		if (arpinfo1->opcode != arpinfo2->opcode)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_HTYPE) {
+		if (arpinfo1->htype != arpinfo2->htype)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_PTYPE) {
+		if (arpinfo1->ptype != arpinfo2->ptype)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_SRC_IP) {
+		if (arpinfo1->saddr != arpinfo2->saddr)
+			return 0;
+		if (arpinfo1->smsk != arpinfo2->smsk)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_DST_IP) {
+		if (arpinfo1->daddr != arpinfo2->daddr)
+			return 0;
+		if (arpinfo1->dmsk != arpinfo2->dmsk)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) {
+		if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN))
+			return 0;
+		if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN))
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_DST_MAC) {
+		if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN))
+			return 0;
+		if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN))
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match arp_match =
+{
+	.name		= "arp",
+	.size		= sizeof(struct ebt_arp_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&arp_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_arpreply.c b/userspace/ebtables2/extensions/ebt_arpreply.c
new file mode 100644
index 0000000..c3757f3
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_arpreply.c
@@ -0,0 +1,139 @@
+/* ebt_arpreply
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  August, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include <linux/netfilter_bridge/ebt_arpreply.h>
+
+static int mac_supplied;
+
+#define REPLY_MAC '1'
+#define REPLY_TARGET '2'
+static struct option opts[] =
+{
+	{ "arpreply-mac" ,    required_argument, 0, REPLY_MAC    },
+	{ "arpreply-target" , required_argument, 0, REPLY_TARGET },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"arpreply target options:\n"
+	" --arpreply-mac address           : source MAC of generated reply\n"
+	" --arpreply-target target         : ACCEPT, DROP, RETURN or CONTINUE\n"
+	"                                    (standard target is DROP)\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	replyinfo->target = EBT_DROP;
+	memset(replyinfo->mac, 0, ETH_ALEN);
+	mac_supplied = 0;
+}
+
+#define OPT_REPLY_MAC     0x01
+#define OPT_REPLY_TARGET  0x02
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case REPLY_MAC:
+		ebt_check_option2(flags, OPT_REPLY_MAC);
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --arpreply-mac mac");
+		memcpy(replyinfo->mac, addr, ETH_ALEN);
+		mac_supplied = 1;
+		break;
+	case REPLY_TARGET:
+		ebt_check_option2(flags, OPT_REPLY_TARGET);
+		if (FILL_TARGET(optarg, replyinfo->target))
+			ebt_print_error2("Illegal --arpreply-target target");
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	if (entry->ethproto != ETH_P_ARP || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For ARP replying the protocol must be specified as ARP");
+	} else if (time == 0 && mac_supplied == 0) {
+		ebt_print_error("No arpreply mac supplied");
+	} else if (BASE_CHAIN && replyinfo->target == EBT_RETURN) {
+		ebt_print_error("--arpreply-target RETURN not allowed on base chain");
+	} else {
+		CLEAR_BASE_CHAIN_BIT;
+		if (strcmp(name, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
+			ebt_print_error("arpreply only allowed in PREROUTING");
+	}
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	printf("--arpreply-mac ");
+	ebt_print_mac(replyinfo->mac);
+	if (replyinfo->target == EBT_DROP)
+		return;
+	printf(" --arpreply-target %s", TARGET_NAME(replyinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_arpreply_info *replyinfo1 =
+	   (struct ebt_arpreply_info *)t1->data;
+	struct ebt_arpreply_info *replyinfo2 =
+	   (struct ebt_arpreply_info *)t2->data;
+
+	return memcmp(replyinfo1->mac, replyinfo2->mac, ETH_ALEN) == 0
+		&& replyinfo1->target == replyinfo2->target;
+}
+
+static struct ebt_u_target arpreply_target =
+{
+	.name		= "arpreply",
+	.size		= sizeof(struct ebt_arpreply_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_target(&arpreply_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_inat.c b/userspace/ebtables2/extensions/ebt_inat.c
new file mode 100644
index 0000000..1aa9435
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_inat.c
@@ -0,0 +1,387 @@
+/* ebt_inat
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <getopt.h>
+#include <ctype.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_inat.h>
+
+static int s_sub_supplied, d_sub_supplied;
+
+#define NAT_S '1'
+#define NAT_D '1'
+#define NAT_S_SUB '2'
+#define NAT_D_SUB '2'
+#define NAT_S_TARGET '3'
+#define NAT_D_TARGET '3'
+static struct option opts_s[] =
+{
+	{ "isnat-list"     , required_argument, 0, NAT_S },
+	{ "isnat-sub"      , required_argument, 0, NAT_S_SUB },
+	{ "isnat-default-target"   , required_argument, 0, NAT_S_TARGET },
+	{ 0 }
+};
+
+static struct option opts_d[] =
+{
+	{ "idnat-list"     , required_argument, 0, NAT_D },
+	{ "idnat-sub"      , required_argument, 0, NAT_D_SUB },
+	{ "idnat-default-target"   , required_argument, 0, NAT_D_TARGET },
+	{ 0 }
+};
+
+static void print_help_common(const char *cas)
+{
+	printf(
+"isnat options:\n"
+" --i%1.1snat-list                    : indexed list of MAC addresses\n"
+" --i%1.1snat-sub                     : /24 subnet to which the rule apply\n"
+" --i%1.1snat-default-target target   : ACCEPT, DROP, RETURN or CONTINUE\n"
+"Indexed list of addresses is as follows:\n"
+"\tlist := chunk\n"
+"\tlist := list chunk\n"
+"\tchunk := pair ','\n"
+"\tpair := index '=' action\n"
+"\taction := mac_addr\n"
+"\taction := mac_addr '+'\n"
+"\taction := '_'\n"
+"where\n"
+"\tindex -- an integer [0..255]\n"
+"\tmac_addr -- a MAC address in format xx:xx:xx:xx:xx:xx\n"
+"If '_' at some index is specified, packets with last %s IP address byte\n"
+"equal to index are DROPped. If there is a MAC address, they are %1.1snatted\n"
+"to this and the target is CONTINUE. If this MAC is followed by '+', the\n"
+"target is ACCEPT.\n"
+"For example,\n"
+"--idnat-list 2=20:21:22:23:24:25,4=_,7=30:31:32:33:34:35,\n"
+"is valid.\n"
+"The subnet MUST be specified. Only packets with 3 first bytes of their\n"
+"%s address are considered. --i%1.1snat-sub parameter must begin with\n"
+"3 integers separated by dots. Only they are considered, the rest is ignored.\n"
+"No matter if you write '192.168.42.', '192.168.42.0', '192.168.42.12',\n"
+"'192.168.42.0/24', '192.168.42.0/23' or '192.168.42.i%1.1snat_sucks!!!',\n"
+"The numbers 192, 168 and 42 are taken and it behaves like a /24 IP subnet.\n"
+"--i%1.1snat-default-target affects only the packet not matching the subnet.\n",
+		cas, cas, cas, cas, cas, cas, cas, cas,
+		cas, cas, cas, cas, cas, cas, cas, cas
+	);
+}
+
+static void print_help_s()
+{
+	print_help_common("src");
+}
+
+static void print_help_d()
+{
+	print_help_common("dest");
+}
+
+static void init_s(struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	s_sub_supplied = 0;
+	memset(natinfo, 0, sizeof(struct ebt_inat_info));
+	natinfo->target = EBT_CONTINUE;
+	return;
+}
+
+static void init_d(struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	d_sub_supplied = 0;
+	memset(natinfo, 0, sizeof(struct ebt_inat_info));
+	natinfo->target = EBT_CONTINUE;
+	return;
+}
+
+static void parse_list(const char *arg, struct ebt_inat_info *info)
+{
+	int i;
+	char c;
+	int count = 0;
+	int now_index = 1;
+	int index;
+	char buf[4];
+	unsigned char mac[6];
+	int ibuf = 0;
+	int imac = 0;
+	int target;
+	memset(buf, 0, 4);
+	i = 0;
+	while (1) {
+		c = arg[i];
+		if (now_index) {
+			if (isdigit(c)) {
+				buf[ibuf++] = c;
+				if (ibuf > 3) {
+					print_error("Index too long at position %d", i);
+				}
+				goto next;
+			}
+			if (c == '=') {
+				if (ibuf == 0) {
+					print_error("Integer index expected before '=' at position %d", i);
+				}
+				buf[ibuf] = 0;
+				ibuf = 0;
+				index = atoi(buf);
+				if (index < 0 || 255 < index) {
+					print_error("Index out of range [0..255], namely %d", index);
+				}
+				now_index = 0;
+				memset(mac, 0, 6);
+				imac = 0;
+				target = EBT_CONTINUE;
+				goto next;
+			}
+			if (c == '\0') {
+				goto next;
+			}
+			print_error("Unexpected '%c' where integer or '=' expected", c);
+		}
+		else {
+			if (isxdigit(c)) {
+				buf[ibuf++] = c;
+				if (ibuf > 2) {
+					print_error("MAC address chunk too long at position %d", i);
+				}
+				goto next;
+			}
+			if (c == ':' || c == ',' || c == '\0') {
+				buf[ibuf] = 0;
+				ibuf = 0;
+				mac[imac++] = strtol(buf, 0, 16);
+				if (c == ',' || c == '\0') {
+					info->a[index].enabled = 1;
+					info->a[index].target = target;
+					memcpy(info->a[index].mac, mac, 6);
+					now_index = 1;
+					count++;
+					goto next;
+				}
+				if (c == ':' && imac >= 6) {
+					print_error("Too many MAC address chunks at position %d", i);
+				}
+				goto next;
+			}
+			if (c == '_') {
+				target = EBT_DROP;
+				goto next;
+			}
+			if (c == '+') {
+				target = EBT_ACCEPT;
+				goto next;
+			}
+			print_error("Unexpected '%c' where hex digit, '_', '+', ',' or end of string expected", c);
+		}
+	next:
+		if (!c) break;
+		i++;
+	}
+	if (count == 0) {
+		print_error("List empty");
+	}
+}
+
+static uint32_t parse_ip(const char *s)
+{
+	int a0, a1, a2;
+	char ip[4];
+	sscanf(s, "%d.%d.%d", &a0, &a1, &a2);
+	ip[0] = a0;
+	ip[1] = a1;
+	ip[2] = a2;
+	ip[3] = 0;
+	return *(uint32_t*)ip;
+}
+
+#define OPT_ISNAT         0x01
+#define OPT_ISNAT_SUB     0x02
+#define OPT_ISNAT_TARGET  0x04
+static int parse_s(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_S:
+		check_option(flags, OPT_ISNAT);
+		parse_list(optarg, natinfo);
+		break;
+	case NAT_S_TARGET:
+		check_option(flags, OPT_ISNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			print_error("Illegal --isnat-default-target target");
+		break;
+	case NAT_S_SUB:
+		natinfo->ip_subnet = parse_ip(optarg);
+		s_sub_supplied = 1;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+#define OPT_IDNAT        0x01
+#define OPT_IDNAT_SUB    0x02
+#define OPT_IDNAT_TARGET 0x04
+static int parse_d(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_D:
+		check_option(flags, OPT_IDNAT);
+		parse_list(optarg, natinfo);
+		break;
+	case NAT_D_TARGET:
+		check_option(flags, OPT_IDNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			print_error("Illegal --idnat-default-target target");
+		break;
+	case NAT_D_SUB:
+		natinfo->ip_subnet = parse_ip(optarg);
+		d_sub_supplied = 1;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+		print_error("--isnat-default-target RETURN not allowed on base chain");
+	CLEAR_BASE_CHAIN_BIT;
+	if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+		print_error("Wrong chain for isnat");
+	if (time == 0 && s_sub_supplied == 0)
+		print_error("No isnat subnet supplied");
+}
+
+static void final_check_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+		print_error("--idnat-default-target RETURN not allowed on base chain");
+	CLEAR_BASE_CHAIN_BIT;
+	if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
+	   || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+		print_error("Wrong chain for idnat");
+	if (time == 0 && d_sub_supplied == 0)
+		print_error("No idnat subnet supplied");
+}
+
+static void print_list(const struct ebt_inat_info *info)
+{
+	int i;
+	for (i = 0; i < 256; i++) {
+		if (info->a[i].enabled) {
+			printf("%d=", i);
+			if (info->a[i].target == EBT_DROP) {
+				printf("_");
+			}
+			else {
+				if (info->a[i].target == EBT_ACCEPT) {
+					printf("+");
+				}
+				print_mac(info->a[i].mac);
+			}
+			printf(",");
+		}
+	}
+}
+
+static void print_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
+
+	unsigned char sub[4];
+	*(uint32_t*)sub = info->ip_subnet;
+	printf("--isnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
+	printf(" --isnat-list ");
+	print_list(info);
+	printf(" --isnat-default-target %s", TARGET_NAME(info->target));
+}
+
+static void print_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
+
+	unsigned char sub[4];
+	*(uint32_t*)sub = info->ip_subnet;
+	printf("--idnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
+	printf(" --idnat-list ");
+	print_list(info);
+	printf(" --idnat-default-target %s", TARGET_NAME(info->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_inat_info *natinfo1 = (struct ebt_inat_info *)t1->data;
+	struct ebt_inat_info *natinfo2 = (struct ebt_inat_info *)t2->data;
+
+
+	return !memcmp(natinfo1, natinfo2, sizeof(struct ebt_inat_info));
+}
+
+static struct ebt_u_target isnat_target =
+{
+	.name		= EBT_ISNAT_TARGET,
+	.size		= sizeof(struct ebt_inat_info),
+	.help		= print_help_s,
+	.init		= init_s,
+	.parse		= parse_s,
+	.final_check	= final_check_s,
+	.print		= print_s,
+	.compare	= compare,
+	.extra_ops	= opts_s,
+};
+
+static struct ebt_u_target idnat_target =
+{
+	.name		= EBT_IDNAT_TARGET,
+	.size		= sizeof(struct ebt_inat_info),
+	.help		= print_help_d,
+	.init		= init_d,
+	.parse		= parse_d,
+	.final_check	= final_check_d,
+	.print		= print_d,
+	.compare	= compare,
+	.extra_ops	= opts_d,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&isnat_target);
+	register_target(&idnat_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_ip.c b/userspace/ebtables2/extensions/ebt_ip.c
new file mode 100644
index 0000000..4e0b7f0
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_ip.c
@@ -0,0 +1,344 @@
+/* ebt_ip
+ * 
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * Changes:
+ *    added ip-sport and ip-dport; parsing of port arguments is
+ *    based on code from iptables-1.2.7a
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_ip.h>
+
+#define IP_SOURCE '1'
+#define IP_DEST   '2'
+#define IP_myTOS  '3' /* include/bits/in.h seems to already define IP_TOS */
+#define IP_PROTO  '4'
+#define IP_SPORT  '5'
+#define IP_DPORT  '6'
+
+static struct option opts[] =
+{
+	{ "ip-source"           , required_argument, 0, IP_SOURCE },
+	{ "ip-src"              , required_argument, 0, IP_SOURCE },
+	{ "ip-destination"      , required_argument, 0, IP_DEST   },
+	{ "ip-dst"              , required_argument, 0, IP_DEST   },
+	{ "ip-tos"              , required_argument, 0, IP_myTOS  },
+	{ "ip-protocol"         , required_argument, 0, IP_PROTO  },
+	{ "ip-proto"            , required_argument, 0, IP_PROTO  },
+	{ "ip-source-port"      , required_argument, 0, IP_SPORT  },
+	{ "ip-sport"            , required_argument, 0, IP_SPORT  },
+	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
+	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
+	{ 0 }
+};
+
+/* put the mask into 4 bytes */
+/* transform a protocol and service name into a port number */
+static uint16_t parse_port(const char *protocol, const char *name)
+{
+	struct servent *service;
+	char *end;
+	int port;
+
+	port = strtol(name, &end, 10);
+	if (*end != '\0') {
+		if (protocol && 
+		    (service = getservbyname(name, protocol)) != NULL)
+			return ntohs(service->s_port);
+	}
+	else if (port >= 0 || port <= 0xFFFF) {
+		return port;
+	}
+	ebt_print_error("Problem with specified %s port '%s'", 
+			protocol?protocol:"", name);
+	return 0;
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+	
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = parse_port(protocol, buffer);
+	else {
+		*cp = '\0';
+		cp++;
+		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		
+		if (ports[0] > ports[1])
+			ebt_print_error("Invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+static void print_port_range(uint16_t *ports)
+{
+	if (ports[0] == ports[1])
+		printf("%d ", ports[0]);
+	else
+		printf("%d:%d ", ports[0], ports[1]);
+}
+
+static void print_help()
+{
+	printf(
+"ip options:\n"
+"--ip-src    [!] address[/mask]: ip source specification\n"
+"--ip-dst    [!] address[/mask]: ip destination specification\n"
+"--ip-tos    [!] tos           : ip tos specification\n"
+"--ip-proto  [!] protocol      : ip protocol specification\n"
+"--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
+"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
+	ipinfo->invflags = 0;
+	ipinfo->bitmask = 0;
+}
+
+#define OPT_SOURCE 0x01
+#define OPT_DEST   0x02
+#define OPT_TOS    0x04
+#define OPT_PROTO  0x08
+#define OPT_SPORT  0x10
+#define OPT_DPORT  0x20
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		ebt_check_option2(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP_SOURCE;
+
+	case IP_DEST:
+		if (c == IP_DEST) {
+			ebt_check_option2(flags, OPT_DEST);
+			ipinfo->bitmask |= EBT_IP_DEST;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == IP_SOURCE)
+				ipinfo->invflags |= EBT_IP_SOURCE;
+			else
+				ipinfo->invflags |= EBT_IP_DEST;
+		}
+		if (c == IP_SOURCE)
+			ebt_parse_ip_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
+		else
+			ebt_parse_ip_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
+		break;
+
+	case IP_SPORT:
+	case IP_DPORT:
+		if (c == IP_SPORT) {
+			ebt_check_option2(flags, OPT_SPORT);
+			ipinfo->bitmask |= EBT_IP_SPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP_SPORT;
+		} else {
+			ebt_check_option2(flags, OPT_DPORT);
+			ipinfo->bitmask |= EBT_IP_DPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP_DPORT;
+		}
+		if (c == IP_SPORT)
+			parse_port_range(NULL, optarg, ipinfo->sport);
+		else
+			parse_port_range(NULL, optarg, ipinfo->dport);
+		break;
+
+	case IP_myTOS:
+		ebt_check_option2(flags, OPT_TOS);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_TOS;
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i > 255 || *end != '\0')
+			ebt_print_error2("Problem with specified IP tos");
+		ipinfo->tos = i;
+		ipinfo->bitmask |= EBT_IP_TOS;
+		break;
+
+	case IP_PROTO:
+		ebt_check_option2(flags, OPT_PROTO);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_PROTO;
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			struct protoent *pe;
+
+			pe = getprotobyname(optarg);
+			if (pe == NULL)
+				ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
+			ipinfo->protocol = pe->p_proto;
+		} else {
+			ipinfo->protocol = (unsigned char) i;
+		}
+		ipinfo->bitmask |= EBT_IP_PROTO;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
+	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For IP filtering the protocol must be "
+		            "specified as IPv4");
+	} else if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
+		(!(ipinfo->bitmask & EBT_IP_PROTO) ||
+		ipinfo->invflags & EBT_IP_PROTO ||
+		(ipinfo->protocol!=IPPROTO_TCP &&
+		 ipinfo->protocol!=IPPROTO_UDP &&
+		 ipinfo->protocol!=IPPROTO_SCTP &&
+		 ipinfo->protocol!=IPPROTO_DCCP)))
+		ebt_print_error("For port filtering the IP protocol must be "
+				"either 6 (tcp), 17 (udp), 33 (dccp) or "
+				"132 (sctp)");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+	int j;
+
+	if (ipinfo->bitmask & EBT_IP_SOURCE) {
+		printf("--ip-src ");
+		if (ipinfo->invflags & EBT_IP_SOURCE)
+			printf("! ");
+		for (j = 0; j < 4; j++)
+			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
+			   (j == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(ipinfo->smsk));
+	}
+	if (ipinfo->bitmask & EBT_IP_DEST) {
+		printf("--ip-dst ");
+		if (ipinfo->invflags & EBT_IP_DEST)
+			printf("! ");
+		for (j = 0; j < 4; j++)
+			printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
+			   (j == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(ipinfo->dmsk));
+	}
+	if (ipinfo->bitmask & EBT_IP_TOS) {
+		printf("--ip-tos ");
+		if (ipinfo->invflags & EBT_IP_TOS)
+			printf("! ");
+		printf("0x%02X ", ipinfo->tos);
+	}
+	if (ipinfo->bitmask & EBT_IP_PROTO) {
+		struct protoent *pe;
+
+		printf("--ip-proto ");
+		if (ipinfo->invflags & EBT_IP_PROTO)
+			printf("! ");
+		pe = getprotobynumber(ipinfo->protocol);
+		if (pe == NULL) {
+			printf("%d ", ipinfo->protocol);
+		} else {
+			printf("%s ", pe->p_name);
+		}
+	}
+	if (ipinfo->bitmask & EBT_IP_SPORT) {
+		printf("--ip-sport ");
+		if (ipinfo->invflags & EBT_IP_SPORT)
+			printf("! ");
+		print_port_range(ipinfo->sport);
+	}
+	if (ipinfo->bitmask & EBT_IP_DPORT) {
+		printf("--ip-dport ");
+		if (ipinfo->invflags & EBT_IP_DPORT)
+			printf("! ");
+		print_port_range(ipinfo->dport);
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
+	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
+
+	if (ipinfo1->bitmask != ipinfo2->bitmask)
+		return 0;
+	if (ipinfo1->invflags != ipinfo2->invflags)
+		return 0;
+	if (ipinfo1->bitmask & EBT_IP_SOURCE) {
+		if (ipinfo1->saddr != ipinfo2->saddr)
+			return 0;
+		if (ipinfo1->smsk != ipinfo2->smsk)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_DEST) {
+		if (ipinfo1->daddr != ipinfo2->daddr)
+			return 0;
+		if (ipinfo1->dmsk != ipinfo2->dmsk)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_TOS) {
+		if (ipinfo1->tos != ipinfo2->tos)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_PROTO) {
+		if (ipinfo1->protocol != ipinfo2->protocol)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_SPORT) {
+		if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
+		   ipinfo1->sport[1] != ipinfo2->sport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_DPORT) {
+		if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
+		   ipinfo1->dport[1] != ipinfo2->dport[1])
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match ip_match =
+{
+	.name		= "ip",
+	.size		= sizeof(struct ebt_ip_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&ip_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_ip6.c b/userspace/ebtables2/extensions/ebt_ip6.c
new file mode 100644
index 0000000..0465e77
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_ip6.c
@@ -0,0 +1,562 @@
+/* ebt_ip6
+ * 
+ * Authors:
+ * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
+ * Manohar Castelino <manohar.castelino@intel.com>
+ *
+ * Summary:
+ * This is just a modification of the IPv4 code written by 
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * with the changes required to support IPv6
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_ip6.h>
+
+
+
+#define IP_SOURCE '1'
+#define IP_DEST   '2'
+#define IP_TCLASS '3'
+#define IP_PROTO  '4'
+#define IP_SPORT  '5'
+#define IP_DPORT  '6'
+#define IP_ICMP6  '7'
+
+static const struct option opts[] =
+{
+	{ "ip6-source"           , required_argument, 0, IP_SOURCE },
+	{ "ip6-src"              , required_argument, 0, IP_SOURCE },
+	{ "ip6-destination"      , required_argument, 0, IP_DEST   },
+	{ "ip6-dst"              , required_argument, 0, IP_DEST   },
+	{ "ip6-traffic-class"    , required_argument, 0, IP_TCLASS },
+	{ "ip6-tclass"           , required_argument, 0, IP_TCLASS },
+	{ "ip6-protocol"         , required_argument, 0, IP_PROTO  },
+	{ "ip6-proto"            , required_argument, 0, IP_PROTO  },
+	{ "ip6-source-port"      , required_argument, 0, IP_SPORT  },
+	{ "ip6-sport"            , required_argument, 0, IP_SPORT  },
+	{ "ip6-destination-port" , required_argument, 0, IP_DPORT  },
+	{ "ip6-dport"            , required_argument, 0, IP_DPORT  },
+	{ "ip6-icmp-type"	 , required_argument, 0, IP_ICMP6  },
+	{ 0 }
+};
+
+
+struct icmpv6_names {
+	const char *name;
+	u_int8_t type;
+	u_int8_t code_min, code_max;
+};
+
+static const struct icmpv6_names icmpv6_codes[] = {
+	{ "destination-unreachable", 1, 0, 0xFF },
+	{ "no-route", 1, 0, 0 },
+	{ "communication-prohibited", 1, 1, 1 },
+	{ "address-unreachable", 1, 3, 3 },
+	{ "port-unreachable", 1, 4, 4 },
+
+	{ "packet-too-big", 2, 0, 0xFF },
+
+	{ "time-exceeded", 3, 0, 0xFF },
+	/* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+	{ "ttl-zero-during-transit", 3, 0, 0 },
+	{ "ttl-zero-during-reassembly", 3, 1, 1 },
+
+	{ "parameter-problem", 4, 0, 0xFF },
+	{ "bad-header", 4, 0, 0 },
+	{ "unknown-header-type", 4, 1, 1 },
+	{ "unknown-option", 4, 2, 2 },
+
+	{ "echo-request", 128, 0, 0xFF },
+	/* Alias */ { "ping", 128, 0, 0xFF },
+
+	{ "echo-reply", 129, 0, 0xFF },
+	/* Alias */ { "pong", 129, 0, 0xFF },
+
+	{ "router-solicitation", 133, 0, 0xFF },
+
+	{ "router-advertisement", 134, 0, 0xFF },
+
+	{ "neighbour-solicitation", 135, 0, 0xFF },
+	/* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+	{ "neighbour-advertisement", 136, 0, 0xFF },
+	/* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+	{ "redirect", 137, 0, 0xFF },
+};
+
+/* transform a protocol and service name into a port number */
+static uint16_t parse_port(const char *protocol, const char *name)
+{
+	struct servent *service;
+	char *end;
+	int port;
+
+	port = strtol(name, &end, 10);
+	if (*end != '\0') {
+		if (protocol && 
+		    (service = getservbyname(name, protocol)) != NULL)
+			return ntohs(service->s_port);
+	}
+	else if (port >= 0 || port <= 0xFFFF) {
+		return port;
+	}
+	ebt_print_error("Problem with specified %s port '%s'", 
+			protocol?protocol:"", name);
+	return 0;
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+	
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = parse_port(protocol, buffer);
+	else {
+		*cp = '\0';
+		cp++;
+		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		
+		if (ports[0] > ports[1])
+			ebt_print_error("Invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+static char*
+parse_num(const char *str, long min, long max, long *num)
+{
+	char *end;
+
+	errno = 0;
+	*num = strtol(str, &end, 10);
+	if (errno && (*num == LONG_MIN || *num == LONG_MAX)) {
+		ebt_print_error("Invalid number %s: %s", str, strerror(errno));
+		return NULL;
+	}
+	if (min <= max) {
+		if (*num > max || *num < min) {
+			ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max);
+			return NULL;
+		}
+	}
+	if (*num == 0 && str == end)
+		return NULL;
+	return end;
+}
+
+static char *
+parse_range(const char *str, long min, long max, long num[])
+{
+	char *next;
+
+	next = parse_num(str, min, max, num);
+	if (next == NULL)
+		return NULL;
+	if (next && *next == ':')
+		next = parse_num(next+1, min, max, &num[1]);
+	else
+		num[1] = num[0];
+	return next;
+}
+
+static int
+parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[])
+{
+	static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
+	unsigned int match = limit;
+	unsigned int i;
+	long number[2];
+
+	for (i = 0; i < limit; i++) {
+		if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)))
+			continue;
+		if (match != limit)
+			ebt_print_error("Ambiguous ICMPv6 type `%s':"
+					" `%s' or `%s'?",
+					icmpv6type, icmpv6_codes[match].name,
+					icmpv6_codes[i].name);
+		match = i;
+	}
+
+	if (match < limit) {
+		type[0] = type[1] = icmpv6_codes[match].type;
+		code[0] = icmpv6_codes[match].code_min;
+		code[1] = icmpv6_codes[match].code_max;
+	} else {
+		char *next = parse_range(icmpv6type, 0, 255, number);
+		if (!next) {
+			ebt_print_error("Unknown ICMPv6 type `%s'",
+							icmpv6type);
+			return -1;
+		}
+		type[0] = (uint8_t) number[0];
+		type[1] = (uint8_t) number[1];
+		switch (*next) {
+		case 0:
+			code[0] = 0;
+			code[1] = 255;
+			return 0;
+		case '/':
+			next = parse_range(next+1, 0, 255, number);
+			code[0] = (uint8_t) number[0];
+			code[1] = (uint8_t) number[1];
+			if (next == NULL)
+				return -1;
+			if (next && *next == 0)
+				return 0;
+		/* fallthrough */
+		default:
+			ebt_print_error("unknown character %c", *next);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void print_port_range(uint16_t *ports)
+{
+	if (ports[0] == ports[1])
+		printf("%d ", ports[0]);
+	else
+		printf("%d:%d ", ports[0], ports[1]);
+}
+
+static void print_icmp_code(uint8_t *code)
+{
+	if (code[0] == code[1])
+		printf("/%"PRIu8 " ", code[0]);
+	else
+		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
+}
+
+static void print_icmp_type(uint8_t *type, uint8_t *code)
+{
+	unsigned int i;
+
+	if (type[0] != type[1]) {
+		printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
+		print_icmp_code(code);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) {
+		if (icmpv6_codes[i].type != type[0])
+			continue;
+
+		if (icmpv6_codes[i].code_min == code[0] &&
+		    icmpv6_codes[i].code_max == code[1]) {
+			printf("%s ", icmpv6_codes[i].name);
+			return;
+		}
+	}
+	printf("%"PRIu8, type[0]);
+	print_icmp_code(code);
+}
+
+static void print_icmpv6types(void)
+{
+	unsigned int i;
+        printf("Valid ICMPv6 Types:");
+
+	for (i=0; i < ARRAY_SIZE(icmpv6_codes); i++) {
+		if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
+			if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
+			    && (icmpv6_codes[i].code_max
+			        == icmpv6_codes[i-1].code_max))
+				printf(" (%s)", icmpv6_codes[i].name);
+			else
+				printf("\n   %s", icmpv6_codes[i].name);
+		}
+		else
+			printf("\n%s", icmpv6_codes[i].name);
+	}
+	printf("\n");
+}
+
+static void print_help()
+{
+	printf(
+"ip6 options:\n"
+"--ip6-src    [!] address[/mask]: ipv6 source specification\n"
+"--ip6-dst    [!] address[/mask]: ipv6 destination specification\n"
+"--ip6-tclass [!] tclass        : ipv6 traffic class specification\n"
+"--ip6-proto  [!] protocol      : ipv6 protocol specification\n"
+"--ip6-sport  [!] port[:port]   : tcp/udp source port or port range\n"
+"--ip6-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
+"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
+print_icmpv6types();
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	ipinfo->invflags = 0;
+	ipinfo->bitmask = 0;
+}
+
+#define OPT_SOURCE 0x01
+#define OPT_DEST   0x02
+#define OPT_TCLASS 0x04
+#define OPT_PROTO  0x08
+#define OPT_SPORT  0x10
+#define OPT_DPORT  0x20
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		ebt_check_option2(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP6_SOURCE;
+		if (ebt_check_inverse2(optarg)) {
+		    ipinfo->invflags |= EBT_IP6_SOURCE;
+		}
+		ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
+		break;
+
+	case IP_DEST:
+		ebt_check_option2(flags, OPT_DEST);
+		ipinfo->bitmask |= EBT_IP6_DEST;
+		if (ebt_check_inverse2(optarg)) {
+			ipinfo->invflags |= EBT_IP6_DEST;
+		}
+		ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
+		break;
+
+	case IP_SPORT:
+	case IP_DPORT:
+		if (c == IP_SPORT) {
+			ebt_check_option2(flags, OPT_SPORT);
+			ipinfo->bitmask |= EBT_IP6_SPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP6_SPORT;
+		} else {
+			ebt_check_option2(flags, OPT_DPORT);
+			ipinfo->bitmask |= EBT_IP6_DPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP6_DPORT;
+		}
+		if (c == IP_SPORT)
+			parse_port_range(NULL, optarg, ipinfo->sport);
+		else
+			parse_port_range(NULL, optarg, ipinfo->dport);
+		break;
+
+	case IP_ICMP6:
+		ebt_check_option2(flags, EBT_IP6_ICMP6);
+		ipinfo->bitmask |= EBT_IP6_ICMP6;
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_ICMP6;
+		if (parse_icmpv6(optarg, ipinfo->icmpv6_type, ipinfo->icmpv6_code))
+			return 0;
+		break;
+
+	case IP_TCLASS:
+		ebt_check_option2(flags, OPT_TCLASS);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_TCLASS;
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i > 255 || *end != '\0')
+			ebt_print_error2("Problem with specified IPv6 traffic class");
+		ipinfo->tclass = i;
+		ipinfo->bitmask |= EBT_IP6_TCLASS;
+		break;
+
+	case IP_PROTO:
+		ebt_check_option2(flags, OPT_PROTO);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_PROTO;
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			struct protoent *pe;
+
+			pe = getprotobyname(optarg);
+			if (pe == NULL)
+				ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
+			ipinfo->protocol = pe->p_proto;
+		} else {
+			ipinfo->protocol = (unsigned char) i;
+		}
+		ipinfo->bitmask |= EBT_IP6_PROTO;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For IPv6 filtering the protocol must be "
+		            "specified as IPv6");
+	} else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) &&
+		(!(ipinfo->bitmask & EBT_IP6_PROTO) ||
+		ipinfo->invflags & EBT_IP6_PROTO ||
+		(ipinfo->protocol!=IPPROTO_TCP &&
+		 ipinfo->protocol!=IPPROTO_UDP &&
+		 ipinfo->protocol!=IPPROTO_SCTP &&
+		 ipinfo->protocol!=IPPROTO_DCCP)))
+		ebt_print_error("For port filtering the IP protocol must be "
+				"either 6 (tcp), 17 (udp), 33 (dccp) or "
+				"132 (sctp)");
+	if ((ipinfo->bitmask & EBT_IP6_ICMP6) &&
+	  (!(ipinfo->bitmask & EBT_IP6_PROTO) ||
+	     ipinfo->invflags & EBT_IP6_PROTO ||
+	     ipinfo->protocol != IPPROTO_ICMPV6))
+		ebt_print_error("For ipv6-icmp filtering the IP protocol must be "
+				"58 (ipv6-icmp)");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	if (ipinfo->bitmask & EBT_IP6_SOURCE) {
+		printf("--ip6-src ");
+		if (ipinfo->invflags & EBT_IP6_SOURCE)
+			printf("! ");
+		printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr));
+		printf("/%s ", ebt_ip6_to_numeric(&ipinfo->smsk));
+	}
+	if (ipinfo->bitmask & EBT_IP6_DEST) {
+		printf("--ip6-dst ");
+		if (ipinfo->invflags & EBT_IP6_DEST)
+			printf("! ");
+		printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr));
+		printf("/%s ", ebt_ip6_to_numeric(&ipinfo->dmsk));
+	}
+	if (ipinfo->bitmask & EBT_IP6_TCLASS) {
+		printf("--ip6-tclass ");
+		if (ipinfo->invflags & EBT_IP6_TCLASS)
+			printf("! ");
+		printf("0x%02X ", ipinfo->tclass);
+	}
+	if (ipinfo->bitmask & EBT_IP6_PROTO) {
+		struct protoent *pe;
+
+		printf("--ip6-proto ");
+		if (ipinfo->invflags & EBT_IP6_PROTO)
+			printf("! ");
+		pe = getprotobynumber(ipinfo->protocol);
+		if (pe == NULL) {
+			printf("%d ", ipinfo->protocol);
+		} else {
+			printf("%s ", pe->p_name);
+		}
+	}
+	if (ipinfo->bitmask & EBT_IP6_SPORT) {
+		printf("--ip6-sport ");
+		if (ipinfo->invflags & EBT_IP6_SPORT)
+			printf("! ");
+		print_port_range(ipinfo->sport);
+	}
+	if (ipinfo->bitmask & EBT_IP6_DPORT) {
+		printf("--ip6-dport ");
+		if (ipinfo->invflags & EBT_IP6_DPORT)
+			printf("! ");
+		print_port_range(ipinfo->dport);
+	}
+	if (ipinfo->bitmask & EBT_IP6_ICMP6) {
+		printf("--ip6-icmp-type ");
+		if (ipinfo->invflags & EBT_IP6_ICMP6)
+			printf("! ");
+		print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data;
+	struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data;
+
+	if (ipinfo1->bitmask != ipinfo2->bitmask)
+		return 0;
+	if (ipinfo1->invflags != ipinfo2->invflags)
+		return 0;
+	if (ipinfo1->bitmask & EBT_IP6_SOURCE) {
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr))
+			return 0;
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk))
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_DEST) {
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr))
+			return 0;
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk))
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_TCLASS) {
+		if (ipinfo1->tclass != ipinfo2->tclass)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_PROTO) {
+		if (ipinfo1->protocol != ipinfo2->protocol)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_SPORT) {
+		if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
+		   ipinfo1->sport[1] != ipinfo2->sport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_DPORT) {
+		if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
+		   ipinfo1->dport[1] != ipinfo2->dport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_ICMP6) {
+		if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] ||
+		    ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] ||
+		    ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] ||
+		    ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1])
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match ip6_match =
+{
+	.name		= EBT_IP6_MATCH,
+	.size		= sizeof(struct ebt_ip6_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&ip6_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_limit.c b/userspace/ebtables2/extensions/ebt_limit.c
new file mode 100644
index 0000000..ee40e5c
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_limit.c
@@ -0,0 +1,218 @@
+/* ebt_limit
+ *
+ * Authors:
+ * Tom Marshall <tommy@home.tig-grr.com>
+ *
+ * Mostly copied from iptables' limit match.
+ *
+ * September, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_limit.h>
+
+#define EBT_LIMIT_AVG	"3/hour"
+#define EBT_LIMIT_BURST	5
+
+static int string_to_number(const char *s, unsigned int min, unsigned int max,
+   unsigned int *ret)
+{
+	long number;
+	char *end;
+
+	errno = 0;
+	number = strtol(s, &end, 0);
+	if (*end == '\0' && end != s) {
+		if (errno != ERANGE && min <= number && number <= max) {
+			*ret = number;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+#define FLAG_LIMIT		0x01
+#define FLAG_LIMIT_BURST	0x02
+#define ARG_LIMIT		'1'
+#define ARG_LIMIT_BURST		'2'
+
+static struct option opts[] =
+{
+	{ "limit",		required_argument, 0, ARG_LIMIT },
+	{ "limit-burst",	required_argument, 0, ARG_LIMIT_BURST },
+	{ 0 }
+};
+
+static void print_help(void)
+{
+	printf(
+"limit options:\n"
+"--limit avg                   : max average match rate: default "EBT_LIMIT_AVG"\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--limit-burst number          : number to match in a burst, -1 < number < 10001,\n"
+"                                default %u\n", EBT_LIMIT_BURST);
+}
+
+static int parse_rate(const char *rate, u_int32_t *val)
+{
+	const char *delim;
+	u_int32_t r;
+	u_int32_t mult = 1;  /* Seconds by default. */
+
+	delim = strchr(rate, '/');
+	if (delim) {
+		if (strlen(delim+1) == 0)
+			return 0;
+
+		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+			mult = 1;
+		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+			mult = 60;
+		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+			mult = 60*60;
+		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+			mult = 24*60*60;
+		else
+			return 0;
+	}
+	r = atoi(rate);
+	if (!r)
+		return 0;
+
+	/* This would get mapped to infinite (1/day is minimum they
+	   can specify, so we're ok at that end). */
+	if (r / mult > EBT_LIMIT_SCALE)
+		return 0;
+
+	*val = EBT_LIMIT_SCALE * mult / r;
+	return 1;
+}
+
+/* Initialize the match. */
+static void init(struct ebt_entry_match *m)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)m->data;
+
+	parse_rate(EBT_LIMIT_AVG, &r->avg);
+	r->burst = EBT_LIMIT_BURST;
+}
+
+/* FIXME: handle overflow:
+	if (r->avg*r->burst/r->burst != r->avg)
+		exit_error(PARAMETER_PROBLEM,
+			   "Sorry: burst too large for that avg rate.\n");
+*/
+
+static int parse(int c, char **argv, int argc,
+      const struct ebt_u_entry *entry,
+      unsigned int *flags,
+      struct ebt_entry_match **match)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data;
+	unsigned int num;
+
+	switch(c) {
+	case ARG_LIMIT:
+		ebt_check_option2(flags, FLAG_LIMIT);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --limit");
+		if (!parse_rate(optarg, &r->avg))
+			ebt_print_error2("bad rate `%s'", optarg);
+		break;
+
+	case ARG_LIMIT_BURST:
+		ebt_check_option2(flags, FLAG_LIMIT_BURST);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --limit-burst");
+		if (string_to_number(optarg, 0, 10000, &num) == -1)
+			ebt_print_error2("bad --limit-burst `%s'", optarg);
+		r->burst = num;
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+struct rates
+{
+	const char *name;
+	u_int32_t mult;
+};
+
+static struct rates g_rates[] =
+{
+	{ "day", EBT_LIMIT_SCALE*24*60*60 },
+	{ "hour", EBT_LIMIT_SCALE*60*60 },
+	{ "min", EBT_LIMIT_SCALE*60 },
+	{ "sec", EBT_LIMIT_SCALE }
+};
+
+static void print_rate(u_int32_t period)
+{
+	unsigned int i;
+
+	for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++)
+		if (period > g_rates[i].mult ||
+		    g_rates[i].mult/period < g_rates[i].mult%period)
+			break;
+
+	printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name);
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
+
+	printf("--limit ");
+	print_rate(r->avg);
+	printf("--limit-burst %u ", r->burst);
+}
+
+static int compare(const struct ebt_entry_match* m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_limit_info* li1 = (struct ebt_limit_info*)m1->data;
+	struct ebt_limit_info* li2 = (struct ebt_limit_info*)m2->data;
+
+	if (li1->avg != li2->avg)
+		return 0;
+
+	if (li1->burst != li2->burst)
+		return 0;
+
+	return 1;
+}
+
+static struct ebt_u_match limit_match =
+{
+	.name		= "limit",
+	.size		= sizeof(struct ebt_limit_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&limit_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_log.c b/userspace/ebtables2/extensions/ebt_log.c
new file mode 100644
index 0000000..1cf831a
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_log.c
@@ -0,0 +1,223 @@
+/* ebt_log
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_log.h>
+
+/*
+ * copied from syslog.h
+ * used for the LOG target
+ */
+#define	LOG_EMERG	0 /* system is unusable               */
+#define	LOG_ALERT	1 /* action must be taken immediately */
+#define	LOG_CRIT	2 /* critical conditions              */
+#define	LOG_ERR		3 /* error conditions                 */
+#define	LOG_WARNING	4 /* warning conditions               */
+#define	LOG_NOTICE	5 /* normal but significant condition */
+#define	LOG_INFO	6 /* informational                    */
+#define	LOG_DEBUG	7 /* debug-level messages             */
+
+#define LOG_DEFAULT_LEVEL LOG_INFO
+
+typedef struct _code {
+	char *c_name;
+	int c_val;
+} CODE;
+
+static CODE eight_priority[] = {
+	{ "emerg", LOG_EMERG },
+	{ "alert", LOG_ALERT },
+	{ "crit", LOG_CRIT },
+	{ "error", LOG_ERR },
+	{ "warning", LOG_WARNING },
+	{ "notice", LOG_NOTICE },
+	{ "info", LOG_INFO },
+	{ "debug", LOG_DEBUG }
+};
+
+static int name_to_loglevel(char* arg)
+{
+	int i;
+	
+	for (i = 0; i < 8; i++)
+		if (!strcmp(arg, eight_priority[i].c_name))
+			return eight_priority[i].c_val;
+	/* return bad loglevel */
+	return 9;
+}
+
+#define LOG_PREFIX '1'
+#define LOG_LEVEL  '2'
+#define LOG_ARP    '3'
+#define LOG_IP     '4'
+#define LOG_LOG    '5'
+#define LOG_IP6    '6'
+static struct option opts[] =
+{
+	{ "log-prefix", required_argument, 0, LOG_PREFIX },
+	{ "log-level" , required_argument, 0, LOG_LEVEL  },
+	{ "log-arp"   , no_argument      , 0, LOG_ARP    },
+	{ "log-ip"    , no_argument      , 0, LOG_IP     },
+	{ "log"       , no_argument      , 0, LOG_LOG    },
+	{ "log-ip6"   , no_argument      , 0, LOG_IP6    },
+	{ 0 }
+};
+
+static void print_help()
+{
+	int i;
+
+	printf(
+"log options:\n"
+"--log               : use this if you're not specifying anything\n"
+"--log-level level   : level = [1-8] or a string\n"
+"--log-prefix prefix : max. %d chars.\n"
+"--log-ip            : put ip info. in the log for ip packets\n"
+"--log-arp           : put (r)arp info. in the log for (r)arp packets\n"
+"--log-ip6           : put ip6 info. in the log for ip6 packets\n"
+	, EBT_LOG_PREFIX_SIZE - 1);
+	printf("levels:\n");
+	for (i = 0; i < 8; i++)
+		printf("%d = %s\n", eight_priority[i].c_val,
+		   eight_priority[i].c_name);
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+
+	loginfo->bitmask = 0;
+	loginfo->prefix[0] = '\0';
+	loginfo->loglevel = LOG_NOTICE;
+}
+
+#define OPT_PREFIX 0x01
+#define OPT_LEVEL  0x02
+#define OPT_ARP    0x04
+#define OPT_IP     0x08
+#define OPT_LOG    0x10
+#define OPT_IP6    0x20
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_watcher **watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)(*watcher)->data;
+	long int i;
+	char *end;
+
+	switch (c) {
+	case LOG_PREFIX:
+		ebt_check_option2(flags, OPT_PREFIX);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-prefix");
+		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+			ebt_print_error2("Prefix too long");
+		if (strchr(optarg, '\"'))
+			ebt_print_error2("Use of \\\" is not allowed in the prefix");
+		strcpy((char *)loginfo->prefix, (char *)optarg);
+		break;
+
+	case LOG_LEVEL:
+		ebt_check_option2(flags, OPT_LEVEL);
+		i = strtol(optarg, &end, 16);
+		if (*end != '\0' || i < 0 || i > 7)
+			loginfo->loglevel = name_to_loglevel(optarg);
+		else
+			loginfo->loglevel = i;
+		if (loginfo->loglevel == 9)
+			ebt_print_error2("Problem with the log-level");
+		break;
+
+	case LOG_IP:
+		ebt_check_option2(flags, OPT_IP);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-ip");
+		loginfo->bitmask |= EBT_LOG_IP;
+		break;
+
+	case LOG_ARP:
+		ebt_check_option2(flags, OPT_ARP);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-arp");
+		loginfo->bitmask |= EBT_LOG_ARP;
+		break;
+
+	case LOG_LOG:
+		ebt_check_option2(flags, OPT_LOG);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log");
+		break;
+
+	case LOG_IP6:
+		ebt_check_option2(flags, OPT_IP6);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-ip6");
+		loginfo->bitmask |= EBT_LOG_IP6;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+
+	printf("--log-level %s --log-prefix \"%s\"",
+		eight_priority[loginfo->loglevel].c_name,
+		loginfo->prefix);
+	if (loginfo->bitmask & EBT_LOG_IP)
+		printf(" --log-ip");
+	if (loginfo->bitmask & EBT_LOG_ARP)
+		printf(" --log-arp");
+	if (loginfo->bitmask & EBT_LOG_IP6)
+		printf(" --log-ip6");
+	printf(" ");
+}
+
+static int compare(const struct ebt_entry_watcher *w1,
+   const struct ebt_entry_watcher *w2)
+{
+	struct ebt_log_info *loginfo1 = (struct ebt_log_info *)w1->data;
+	struct ebt_log_info *loginfo2 = (struct ebt_log_info *)w2->data;
+
+	if (loginfo1->loglevel != loginfo2->loglevel)
+		return 0;
+	if (loginfo1->bitmask != loginfo2->bitmask)
+		return 0;
+	return !strcmp((char *)loginfo1->prefix, (char *)loginfo2->prefix);
+}
+
+static struct ebt_u_watcher log_watcher =
+{
+	.name		= "log",
+	.size		= sizeof(struct ebt_log_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_watcher(&log_watcher);
+}
diff --git a/userspace/ebtables2/extensions/ebt_mark.c b/userspace/ebtables2/extensions/ebt_mark.c
new file mode 100644
index 0000000..5776b1c
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_mark.c
@@ -0,0 +1,178 @@
+/* ebt_mark
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002, September 2006
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+
+static int mark_supplied;
+
+#define MARK_TARGET  '1'
+#define MARK_SETMARK '2'
+#define MARK_ORMARK  '3'
+#define MARK_ANDMARK '4'
+#define MARK_XORMARK '5'
+static struct option opts[] =
+{
+	{ "mark-target" , required_argument, 0, MARK_TARGET },
+	/* an oldtime messup, we should have always used the scheme
+	 * <extension-name>-<option> */
+	{ "set-mark"    , required_argument, 0, MARK_SETMARK },
+	{ "mark-set"    , required_argument, 0, MARK_SETMARK },
+	{ "mark-or"     , required_argument, 0, MARK_ORMARK  },
+	{ "mark-and"    , required_argument, 0, MARK_ANDMARK },
+	{ "mark-xor"    , required_argument, 0, MARK_XORMARK },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"mark target options:\n"
+	" --mark-set value     : Set nfmark value\n"
+	" --mark-or  value     : Or nfmark with value (nfmark |= value)\n"
+	" --mark-and value     : And nfmark with value (nfmark &= value)\n"
+	" --mark-xor value     : Xor nfmark with value (nfmark ^= value)\n"
+	" --mark-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+
+	markinfo->target = EBT_ACCEPT;
+	markinfo->mark = 0;
+	mark_supplied = 0;
+}
+
+#define OPT_MARK_TARGET   0x01
+#define OPT_MARK_SETMARK  0x02
+#define OPT_MARK_ORMARK   0x04
+#define OPT_MARK_ANDMARK  0x08
+#define OPT_MARK_XORMARK  0x10
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)(*target)->data;
+	char *end;
+
+	switch (c) {
+	case MARK_TARGET:
+		{ int tmp;
+		ebt_check_option2(flags, OPT_MARK_TARGET);
+		if (FILL_TARGET(optarg, tmp))
+			ebt_print_error2("Illegal --mark-target target");
+		/* the 4 lsb are left to designate the target */
+		markinfo->target = (markinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS);
+		}
+		return 1;
+	case MARK_SETMARK:
+		ebt_check_option2(flags, OPT_MARK_SETMARK);
+		if (*flags & (OPT_MARK_ORMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-set cannot be used together with specific --mark option");
+                break;
+	case MARK_ORMARK:
+		ebt_check_option2(flags, OPT_MARK_ORMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-or cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_OR_VALUE;
+                break;
+	case MARK_ANDMARK:
+		ebt_check_option2(flags, OPT_MARK_ANDMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ORMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-and cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_AND_VALUE;
+                break;
+	case MARK_XORMARK:
+		ebt_check_option2(flags, OPT_MARK_XORMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_ORMARK))
+			ebt_print_error2("--mark-xor cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_XOR_VALUE;
+                break;
+	 default:
+		return 0;
+	}
+	/* mutual code */
+	markinfo->mark = strtoul(optarg, &end, 0);
+	if (*end != '\0' || end == optarg)
+		ebt_print_error2("Bad MARK value '%s'", optarg);
+	mark_supplied = 1;
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+
+	if (time == 0 && mark_supplied == 0) {
+		ebt_print_error("No mark value supplied");
+	} else if (BASE_CHAIN && (markinfo->target|~EBT_VERDICT_BITS) == EBT_RETURN)
+		ebt_print_error("--mark-target RETURN not allowed on base chain");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+	int tmp;
+
+	tmp = markinfo->target & ~EBT_VERDICT_BITS;
+	if (tmp == MARK_SET_VALUE)
+		printf("--mark-set");
+	else if (tmp == MARK_OR_VALUE)
+		printf("--mark-or");
+	else if (tmp == MARK_XOR_VALUE)
+		printf("--mark-xor");
+	else if (tmp == MARK_AND_VALUE)
+		printf("--mark-and");
+	else
+		ebt_print_error("oops, unknown mark action, try a later version of ebtables");
+	printf(" 0x%lx", markinfo->mark);
+	tmp = markinfo->target | ~EBT_VERDICT_BITS;
+	printf(" --mark-target %s", TARGET_NAME(tmp));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_mark_t_info *markinfo1 =
+	   (struct ebt_mark_t_info *)t1->data;
+	struct ebt_mark_t_info *markinfo2 =
+	   (struct ebt_mark_t_info *)t2->data;
+
+	return markinfo1->target == markinfo2->target &&
+	   markinfo1->mark == markinfo2->mark;
+}
+
+static struct ebt_u_target mark_target =
+{
+	.name		= "mark",
+	.size		= sizeof(struct ebt_mark_t_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_target(&mark_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_mark_m.c b/userspace/ebtables2/extensions/ebt_mark_m.c
new file mode 100644
index 0000000..2a259b0
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_mark_m.c
@@ -0,0 +1,127 @@
+/* ebt_mark_m
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+
+#define MARK '1'
+
+static struct option opts[] =
+{
+	{ "mark", required_argument, 0, MARK },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"mark option:\n"
+"--mark    [!] [value][/mask]: Match nfmask value (see man page)\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)match->data;
+
+	markinfo->mark    = 0;
+	markinfo->mask    = 0;
+	markinfo->invert  = 0;
+	markinfo->bitmask = 0;
+}
+
+#define OPT_MARK 0x01
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)
+	   (*match)->data;
+	char *end;
+
+	switch (c) {
+	case MARK:
+		ebt_check_option2(flags, MARK);
+		if (ebt_check_inverse2(optarg))
+			markinfo->invert = 1;
+		markinfo->mark = strtoul(optarg, &end, 0);
+		markinfo->bitmask = EBT_MARK_AND;
+		if (*end == '/') {
+			if (end == optarg)
+				markinfo->bitmask = EBT_MARK_OR;
+			markinfo->mask = strtoul(end+1, &end, 0);
+		} else
+			markinfo->mask = 0xffffffff;
+		if ( *end != '\0' || end == optarg)
+			ebt_print_error2("Bad mark value '%s'", optarg);
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_mark_m_info *markinfo =
+	   (struct ebt_mark_m_info *)match->data;
+
+	printf("--mark ");
+	if (markinfo->invert)
+		printf("! ");
+	if (markinfo->bitmask == EBT_MARK_OR)
+		printf("/0x%lx ", markinfo->mask);
+	else if(markinfo->mask != 0xffffffff)
+		printf("0x%lx/0x%lx ", markinfo->mark, markinfo->mask);
+	else
+		printf("0x%lx ", markinfo->mark);
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_mark_m_info *markinfo1 = (struct ebt_mark_m_info *)m1->data;
+	struct ebt_mark_m_info *markinfo2 = (struct ebt_mark_m_info *)m2->data;
+
+	if (markinfo1->invert != markinfo2->invert)
+		return 0;
+	if (markinfo1->mark != markinfo2->mark)
+		return 0;
+	if (markinfo1->mask != markinfo2->mask)
+		return 0;
+	if (markinfo1->bitmask != markinfo2->bitmask)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match mark_match =
+{
+	.name		= "mark_m",
+	.size		= sizeof(struct ebt_mark_m_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&mark_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_nat.c b/userspace/ebtables2/extensions/ebt_nat.c
new file mode 100644
index 0000000..e6afbf8
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_nat.c
@@ -0,0 +1,238 @@
+/* ebt_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * June, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+
+static int to_source_supplied, to_dest_supplied;
+
+#define NAT_S '1'
+#define NAT_D '1'
+#define NAT_S_TARGET '2'
+#define NAT_D_TARGET '2'
+#define NAT_S_ARP '3'
+static struct option opts_s[] =
+{
+	{ "to-source"     , required_argument, 0, NAT_S },
+	{ "to-src"        , required_argument, 0, NAT_S },
+	{ "snat-target"   , required_argument, 0, NAT_S_TARGET },
+	{ "snat-arp"      ,       no_argument, 0, NAT_S_ARP },
+	{ 0 }
+};
+
+static struct option opts_d[] =
+{
+	{ "to-destination", required_argument, 0, NAT_D },
+	{ "to-dst"        , required_argument, 0, NAT_D },
+	{ "dnat-target"   , required_argument, 0, NAT_D_TARGET },
+	{ 0 }
+};
+
+static void print_help_s()
+{
+	printf(
+	"snat options:\n"
+	" --to-src address       : MAC address to map source to\n"
+	" --snat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n"
+	" --snat-arp             : also change src address in arp msg\n");
+}
+
+static void print_help_d()
+{
+	printf(
+	"dnat options:\n"
+	" --to-dst address       : MAC address to map destination to\n"
+	" --dnat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init_s(struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	to_source_supplied = 0;
+	natinfo->target = EBT_ACCEPT;
+	return;
+}
+
+static void init_d(struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	to_dest_supplied = 0;
+	natinfo->target = EBT_ACCEPT;
+	return;
+}
+
+#define OPT_SNAT         0x01
+#define OPT_SNAT_TARGET  0x02
+#define OPT_SNAT_ARP     0x04
+static int parse_s(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case NAT_S:
+		ebt_check_option2(flags, OPT_SNAT);
+		to_source_supplied = 1;
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --to-source mac");
+		memcpy(natinfo->mac, addr, ETH_ALEN);
+		break;
+	case NAT_S_TARGET:
+		{ int tmp;
+		ebt_check_option2(flags, OPT_SNAT_TARGET);
+		if (FILL_TARGET(optarg, tmp))
+			ebt_print_error2("Illegal --snat-target target");
+		natinfo->target = (natinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS);
+		}
+		break;
+	case NAT_S_ARP:
+		ebt_check_option2(flags, OPT_SNAT_ARP);
+		natinfo->target ^= NAT_ARP_BIT;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+#define OPT_DNAT        0x01
+#define OPT_DNAT_TARGET 0x02
+static int parse_d(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case NAT_D:
+		ebt_check_option2(flags, OPT_DNAT);
+		to_dest_supplied = 1;
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --to-destination mac");
+		memcpy(natinfo->mac, addr, ETH_ALEN);
+		break;
+	case NAT_D_TARGET:
+		ebt_check_option2(flags, OPT_DNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			ebt_print_error2("Illegal --dnat-target target");
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	if (BASE_CHAIN && (natinfo->target | ~EBT_VERDICT_BITS) == EBT_RETURN) {
+		ebt_print_error("--snat-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat")) {
+		ebt_print_error("Wrong chain for snat");
+	} else if (time == 0 && to_source_supplied == 0)
+		ebt_print_error("No snat address supplied");
+}
+
+static void final_check_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN) {
+		ebt_print_error("--dnat-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
+	   || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute"))) {
+		ebt_print_error("Wrong chain for dnat");
+	} else if (time == 0 && to_dest_supplied == 0)
+		ebt_print_error("No dnat address supplied");
+}
+
+static void print_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	printf("--to-src ");
+	ebt_print_mac(natinfo->mac);
+	if (!(natinfo->target&NAT_ARP_BIT))
+		printf(" --snat-arp");
+	printf(" --snat-target %s", TARGET_NAME((natinfo->target|~EBT_VERDICT_BITS)));
+}
+
+static void print_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	printf("--to-dst ");
+	ebt_print_mac(natinfo->mac);
+	printf(" --dnat-target %s", TARGET_NAME(natinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data;
+	struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data;
+
+	return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac)) &&
+	   natinfo1->target == natinfo2->target;
+}
+
+static struct ebt_u_target snat_target =
+{
+	.name		= "snat",
+	.size		= sizeof(struct ebt_nat_info),
+	.help		= print_help_s,
+	.init		= init_s,
+	.parse		= parse_s,
+	.final_check	= final_check_s,
+	.print		= print_s,
+	.compare	= compare,
+	.extra_ops	= opts_s,
+};
+
+static struct ebt_u_target dnat_target =
+{
+	.name		= "dnat",
+	.size		= sizeof(struct ebt_nat_info),
+	.help		= print_help_d,
+	.init		= init_d,
+	.parse		= parse_d,
+	.final_check	= final_check_d,
+	.print		= print_d,
+	.compare	= compare,
+	.extra_ops	= opts_d,
+};
+
+void _init(void)
+{
+	ebt_register_target(&snat_target);
+	ebt_register_target(&dnat_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_nflog.c b/userspace/ebtables2/extensions/ebt_nflog.c
new file mode 100644
index 0000000..0cd10e0
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_nflog.c
@@ -0,0 +1,172 @@
+/* ebt_nflog
+ *
+ * Authors:
+ * Peter Warasin <peter@endian.com>
+ *
+ *  February, 2008
+ *
+ * Based on:
+ *  ebt_ulog.c, (C) 2004, Bart De Schuymer <bdschuym@pandora.be>
+ *  libxt_NFLOG.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_nflog.h>
+
+enum {
+	NFLOG_GROUP = 0x1,
+	NFLOG_PREFIX = 0x2,
+	NFLOG_RANGE = 0x4,
+	NFLOG_THRESHOLD = 0x8,
+	NFLOG_NFLOG = 0x16,
+};
+
+static struct option nflog_opts[] = {
+	{"nflog-group", required_argument, NULL, NFLOG_GROUP},
+	{"nflog-prefix", required_argument, NULL, NFLOG_PREFIX},
+	{"nflog-range", required_argument, NULL, NFLOG_RANGE},
+	{"nflog-threshold", required_argument, NULL, NFLOG_THRESHOLD},
+	{"nflog", no_argument, NULL, NFLOG_NFLOG},
+	{.name = NULL}
+};
+
+static void nflog_help()
+{
+	printf("nflog options:\n"
+	       "--nflog               : use the default nflog parameters\n"
+	       "--nflog-prefix prefix : Prefix string for log message\n"
+	       "--nflog-group group   : NETLINK group used for logging\n"
+	       "--nflog-range range   : Number of byte to copy\n"
+	       "--nflog-threshold     : Message threshold of"
+	       "in-kernel queue\n");
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data;
+
+	info->prefix[0] = '\0';
+	info->group = EBT_NFLOG_DEFAULT_GROUP;
+	info->threshold = EBT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static int nflog_parse(int c, char **argv, int argc,
+		       const struct ebt_u_entry *entry, unsigned int *flags,
+		       struct ebt_entry_watcher **watcher)
+{
+	struct ebt_nflog_info *info;
+	unsigned int i;
+	char *end;
+
+	info = (struct ebt_nflog_info *)(*watcher)->data;
+	switch (c) {
+	case NFLOG_PREFIX:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_PREFIX);
+		if (strlen(optarg) > EBT_NFLOG_PREFIX_SIZE - 1)
+			ebt_print_error("Prefix too long for nflog-prefix");
+		strcpy(info->prefix, optarg);
+		break;
+
+	case NFLOG_GROUP:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_GROUP);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-group must be a number!");
+		info->group = i;
+		break;
+
+	case NFLOG_RANGE:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_RANGE);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-range must be a number!");
+		info->len = i;
+		break;
+
+	case NFLOG_THRESHOLD:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_THRESHOLD);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-threshold must be a number!");
+		info->threshold = i;
+		break;
+	case NFLOG_NFLOG:
+		if (ebt_check_inverse(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_NFLOG);
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+
+ inverse_invalid:
+	ebt_print_error("The use of '!' makes no sense for the nflog watcher");
+	return 1;
+}
+
+static void nflog_final_check(const struct ebt_u_entry *entry,
+			      const struct ebt_entry_watcher *watcher,
+			      const char *name, unsigned int hookmask,
+			      unsigned int time)
+{
+}
+
+static void nflog_print(const struct ebt_u_entry *entry,
+			const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data;
+
+	if (info->prefix[0] != '\0')
+		printf("--nflog-prefix \"%s\"", info->prefix);
+	if (info->group)
+		printf("--nflog-group %d ", info->group);
+	if (info->len)
+		printf("--nflog-range %d", info->len);
+	if (info->threshold != EBT_NFLOG_DEFAULT_THRESHOLD)
+		printf(" --nflog-threshold %d ", info->threshold);
+}
+
+static int nflog_compare(const struct ebt_entry_watcher *w1,
+			 const struct ebt_entry_watcher *w2)
+{
+	struct ebt_nflog_info *info1 = (struct ebt_nflog_info *)w1->data;
+	struct ebt_nflog_info *info2 = (struct ebt_nflog_info *)w2->data;
+
+	if (info1->group != info2->group ||
+	    info1->len != info2->len ||
+	    info1->threshold != info2->threshold ||
+	    strcmp(info1->prefix, info2->prefix))
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_watcher nflog_watcher = {
+	.name = "nflog",
+	.size = sizeof(struct ebt_nflog_info),
+	.help = nflog_help,
+	.init = init,
+	.parse = nflog_parse,
+	.final_check = nflog_final_check,
+	.print = nflog_print,
+	.compare = nflog_compare,
+	.extra_ops = nflog_opts,
+};
+
+void _init(void)
+{
+	ebt_register_watcher(&nflog_watcher);
+}
diff --git a/userspace/ebtables2/extensions/ebt_pkttype.c b/userspace/ebtables2/extensions/ebt_pkttype.c
new file mode 100644
index 0000000..5b5cb03
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_pkttype.c
@@ -0,0 +1,131 @@
+/* ebt_pkttype
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/if_packet.h>
+#include <linux/netfilter_bridge/ebt_pkttype.h>
+
+char *classes[] =
+{
+	"host",
+	"broadcast",
+	"multicast",
+	"otherhost",
+	"outgoing",
+	"loopback",
+	"fastroute",
+	"\0"
+};
+
+static struct option opts[] =
+{
+	{ "pkttype-type"        , required_argument, 0, '1' },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"pkttype options:\n"
+"--pkttype-type    [!] type: class the packet belongs to\n"
+"Possible values: broadcast, multicast, host, otherhost, or any other byte value (which would be pretty useless).\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
+
+	pt->invert = 0;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_pkttype_info *ptinfo = (struct ebt_pkttype_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case '1':
+		ebt_check_option2(flags, 1);
+		if (ebt_check_inverse2(optarg))
+			ptinfo->invert = 1;
+		i = strtol(optarg, &end, 16);
+		if (*end != '\0') {
+			int j = 0;
+			i = -1;
+			while (classes[j][0])
+				if (!strcasecmp(optarg, classes[j++])) {
+					i = j - 1;
+					break;
+				}
+		}
+		if (i < 0 || i > 255)
+			ebt_print_error2("Problem with specified pkttype class");
+		ptinfo->pkt_type = (uint8_t)i;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
+	int i = 0;
+
+	printf("--pkttype-type %s", pt->invert ? "! " : "");
+	while (classes[i++][0]);
+	if (pt->pkt_type < i - 1)
+		printf("%s ", classes[pt->pkt_type]);
+	else
+		printf("%d ", pt->pkt_type);
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_pkttype_info *pt1 = (struct ebt_pkttype_info *)m1->data;
+	struct ebt_pkttype_info *pt2 = (struct ebt_pkttype_info *)m2->data;
+
+	if (pt1->invert != pt2->invert ||
+	    pt1->pkt_type != pt2->pkt_type)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match pkttype_match =
+{
+	.name		= "pkttype",
+	.size		= sizeof(struct ebt_pkttype_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&pkttype_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_redirect.c b/userspace/ebtables2/extensions/ebt_redirect.c
new file mode 100644
index 0000000..e470818
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_redirect.c
@@ -0,0 +1,114 @@
+/* ebt_redirect
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_redirect.h>
+
+#define REDIRECT_TARGET '1'
+static struct option opts[] =
+{
+	{ "redirect-target", required_argument, 0, REDIRECT_TARGET },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"redirect option:\n"
+	" --redirect-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	redirectinfo->target = EBT_ACCEPT;
+	return;
+}
+
+#define OPT_REDIRECT_TARGET  0x01
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)(*target)->data;
+
+	switch (c) {
+	case REDIRECT_TARGET:
+		ebt_check_option2(flags, OPT_REDIRECT_TARGET);
+		if (FILL_TARGET(optarg, redirectinfo->target))
+			ebt_print_error2("Illegal --redirect-target target");
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	if (BASE_CHAIN && redirectinfo->target == EBT_RETURN) {
+		ebt_print_error("--redirect-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if ( ((hookmask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+		ebt_print_error("Wrong chain for redirect");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	if (redirectinfo->target == EBT_ACCEPT)
+		return;
+	printf(" --redirect-target %s", TARGET_NAME(redirectinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_redirect_info *redirectinfo1 =
+	   (struct ebt_redirect_info *)t1->data;
+	struct ebt_redirect_info *redirectinfo2 =
+	   (struct ebt_redirect_info *)t2->data;
+
+	return redirectinfo1->target == redirectinfo2->target;
+}
+
+static struct ebt_u_target redirect_target =
+{
+	.name		= "redirect",
+	.size		= sizeof(struct ebt_redirect_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_target(&redirect_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_standard.c b/userspace/ebtables2/extensions/ebt_standard.c
new file mode 100644
index 0000000..67d4d7c
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_standard.c
@@ -0,0 +1,90 @@
+/* ebt_standard
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+
+static struct option opts[] =
+{
+	{0}
+};
+
+static void print_help()
+{
+	printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n"
+	       "The target can also be a user defined chain.\n");
+}
+
+static void init(struct ebt_entry_target *t)
+{
+	((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_target **target)
+{
+	return 0;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	int verdict = ((struct ebt_standard_target *)target)->verdict;
+
+	if (verdict >= 0) {
+		struct ebt_u_entries *entries;
+
+		entries = entry->replace->chains[verdict + NF_BR_NUMHOOKS];
+		printf("%s", entries->name);
+		return;
+	}
+	if (verdict == EBT_CONTINUE)
+		printf("CONTINUE ");
+	else if (verdict == EBT_ACCEPT)
+		printf("ACCEPT ");
+	else if (verdict == EBT_DROP)
+		printf("DROP ");
+	else if (verdict == EBT_RETURN)
+		printf("RETURN ");
+	else
+		ebt_print_bug("Bad standard target");
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	return ((struct ebt_standard_target *)t1)->verdict ==
+	   ((struct ebt_standard_target *)t2)->verdict;
+}
+
+static struct ebt_u_target standard =
+{
+	.name		= "standard",
+	.size		= sizeof(struct ebt_standard_target) -
+			  sizeof(struct ebt_entry_target),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_target(&standard);
+}
diff --git a/userspace/ebtables2/extensions/ebt_stp.c b/userspace/ebtables2/extensions/ebt_stp.c
new file mode 100644
index 0000000..2b108a7
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_stp.c
@@ -0,0 +1,343 @@
+/* ebt_stp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_stp.h>
+
+#define STP_TYPE	'a'
+#define STP_FLAGS	'b'
+#define STP_ROOTPRIO	'c'
+#define STP_ROOTADDR	'd'
+#define STP_ROOTCOST	'e'
+#define STP_SENDERPRIO	'f'
+#define STP_SENDERADDR	'g'
+#define STP_PORT	'h'
+#define STP_MSGAGE	'i'
+#define STP_MAXAGE	'j'
+#define STP_HELLOTIME	'k'
+#define STP_FWDD	'l'
+#define STP_NUMOPS 12
+
+static struct option opts[] =
+{
+	{ "stp-type"         , required_argument, 0, STP_TYPE},
+	{ "stp-flags"        , required_argument, 0, STP_FLAGS},
+	{ "stp-root-prio"    , required_argument, 0, STP_ROOTPRIO},
+	{ "stp-root-addr"    , required_argument, 0, STP_ROOTADDR},
+	{ "stp-root-cost"    , required_argument, 0, STP_ROOTCOST},
+	{ "stp-sender-prio"  , required_argument, 0, STP_SENDERPRIO},
+	{ "stp-sender-addr"  , required_argument, 0, STP_SENDERADDR},
+	{ "stp-port"         , required_argument, 0, STP_PORT},
+	{ "stp-msg-age"      , required_argument, 0, STP_MSGAGE},
+	{ "stp-max-age"      , required_argument, 0, STP_MAXAGE},
+	{ "stp-hello-time"   , required_argument, 0, STP_HELLOTIME},
+	{ "stp-forward-delay", required_argument, 0, STP_FWDD},
+	{ 0 }
+};
+
+#define BPDU_TYPE_CONFIG 0
+#define BPDU_TYPE_TCN 0x80
+#define BPDU_TYPE_CONFIG_STRING "config"
+#define BPDU_TYPE_TCN_STRING "tcn"
+
+#define FLAG_TC 0x01
+#define FLAG_TC_ACK 0x80
+#define FLAG_TC_STRING "topology-change"
+#define FLAG_TC_ACK_STRING "topology-change-ack"
+
+static void print_help()
+{
+	printf(
+"stp options:\n"
+"--stp-type type                  : BPDU type\n"
+"--stp-flags flag                 : control flag\n"
+"--stp-root-prio prio[:prio]      : root priority (16-bit) range\n"
+"--stp-root-addr address[/mask]   : MAC address of root\n"
+"--stp-root-cost cost[:cost]      : root cost (32-bit) range\n"
+"--stp-sender-prio prio[:prio]    : sender priority (16-bit) range\n"
+"--stp-sender-addr address[/mask] : MAC address of sender\n"
+"--stp-port port[:port]           : port id (16-bit) range\n"
+"--stp-msg-age age[:age]          : message age timer (16-bit) range\n"
+"--stp-max-age age[:age]          : maximum age timer (16-bit) range\n"
+"--stp-hello-time time[:time]     : hello time timer (16-bit) range\n"
+"--stp-forward-delay delay[:delay]: forward delay timer (16-bit) range\n"
+" Recognized BPDU type strings:\n"
+"   \"config\": configuration BPDU (=0)\n"
+"   \"tcn\"   : topology change notification BPDU (=0x80)\n"
+" Recognized control flag strings:\n"
+"   \"topology-change\"    : topology change flag (0x01)\n"
+"   \"topology-change-ack\": topology change acknowledgement flag (0x80)");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data;
+
+	stpinfo->invflags = 0;
+	stpinfo->bitmask = 0;
+}
+
+static int parse_range(const char *portstring, void *lower, void *upper,
+   int bits, uint32_t min, uint32_t max)
+{
+	char *buffer;
+	char *cp, *end;
+	uint32_t low_nr, upp_nr;
+	int ret = 0;
+
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL) {
+		low_nr = strtoul(buffer, &end, 10);
+		if (*end || low_nr < min || low_nr > max) {
+			ret = -1;
+			goto out;
+		}
+		if (bits == 2) {
+			*(uint16_t *)lower =  low_nr;
+			*(uint16_t *)upper =  low_nr;
+		} else {
+			*(uint32_t *)lower =  low_nr;
+			*(uint32_t *)upper =  low_nr;
+		}
+	} else {
+		*cp = '\0';
+		cp++;
+		if (!*buffer)
+			low_nr = min;
+		else {
+			low_nr = strtoul(buffer, &end, 10);
+			if (*end || low_nr < min) {
+				ret = -1;
+				goto out;
+			}
+		}
+		if (!*cp)
+			upp_nr = max;
+		else {
+			upp_nr = strtoul(cp, &end, 10);
+			if (*end || upp_nr > max) {
+				ret = -1;
+				goto out;
+			}
+		}
+		if (upp_nr < low_nr) {
+			ret = -1;
+			goto out;
+		}
+		if (bits == 2) {
+			*(uint16_t *)lower = low_nr;
+			*(uint16_t *)upper = upp_nr;
+		} else {
+			*(uint32_t *)lower = low_nr;
+			*(uint32_t *)upper = upp_nr;
+		}
+	}
+out:
+	free(buffer);
+	return ret;
+}
+
+static void print_range(unsigned int l, unsigned int u)
+{
+	if (l == u)
+		printf("%u ", l);
+	else
+		printf("%u:%u ", l, u);
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)(*match)->data;
+	unsigned int flag;
+	long int i;
+	char *end = NULL;
+
+	if (c < 'a' || c > ('a' + STP_NUMOPS - 1))
+		return 0;
+	flag = 1 << (c - 'a');
+	ebt_check_option2(flags, flag);
+	if (ebt_check_inverse2(optarg))
+		stpinfo->invflags |= flag;
+	stpinfo->bitmask |= flag;
+	switch (flag) {
+	case EBT_STP_TYPE:
+		i = strtol(optarg, &end, 0);
+		if (i < 0 || i > 255 || *end != '\0') {
+			if (!strcasecmp(optarg, BPDU_TYPE_CONFIG_STRING))
+				stpinfo->type = BPDU_TYPE_CONFIG;
+			else if (!strcasecmp(optarg, BPDU_TYPE_TCN_STRING))
+				stpinfo->type = BPDU_TYPE_TCN;
+			else
+				ebt_print_error2("Bad --stp-type argument");
+		} else
+			stpinfo->type = i;
+		break;
+	case EBT_STP_FLAGS:
+		i = strtol(optarg, &end, 0);
+		if (i < 0 || i > 255 || *end != '\0') {
+			if (!strcasecmp(optarg, FLAG_TC_STRING))
+				stpinfo->config.flags = FLAG_TC;
+			else if (!strcasecmp(optarg, FLAG_TC_ACK_STRING))
+				stpinfo->config.flags = FLAG_TC_ACK;
+			else
+				ebt_print_error2("Bad --stp-flags argument");
+		} else
+			stpinfo->config.flags = i;
+		break;
+	case EBT_STP_ROOTPRIO:
+		if (parse_range(argv[optind-1], &(stpinfo->config.root_priol),
+		    &(stpinfo->config.root_priou), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-root-prio range");
+		break;
+	case EBT_STP_ROOTCOST:
+		if (parse_range(argv[optind-1], &(stpinfo->config.root_costl),
+		    &(stpinfo->config.root_costu), 4, 0, 0xffffffff))
+			ebt_print_error("Bad --stp-root-cost range");
+		break;
+	case EBT_STP_SENDERPRIO:
+		if (parse_range(argv[optind-1], &(stpinfo->config.sender_priol),
+		    &(stpinfo->config.sender_priou), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-sender-prio range");
+		break;
+	case EBT_STP_PORT:
+		if (parse_range(argv[optind-1], &(stpinfo->config.portl),
+		    &(stpinfo->config.portu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-port range");
+		break;
+	case EBT_STP_MSGAGE:
+		if (parse_range(argv[optind-1], &(stpinfo->config.msg_agel),
+		    &(stpinfo->config.msg_ageu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-msg-age range");
+		break;
+	case EBT_STP_MAXAGE:
+		if (parse_range(argv[optind-1], &(stpinfo->config.max_agel),
+		    &(stpinfo->config.max_ageu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-max-age range");
+		break;
+	case EBT_STP_HELLOTIME:
+		if (parse_range(argv[optind-1], &(stpinfo->config.hello_timel),
+		    &(stpinfo->config.hello_timeu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-hello-time range");
+		break;
+	case EBT_STP_FWDD:
+		if (parse_range(argv[optind-1], &(stpinfo->config.forward_delayl),
+		    &(stpinfo->config.forward_delayu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-forward-delay range");
+		break;
+	case EBT_STP_ROOTADDR:
+		if (ebt_get_mac_and_mask(argv[optind-1],
+		    (unsigned char *)stpinfo->config.root_addr,
+		    (unsigned char *)stpinfo->config.root_addrmsk))
+			ebt_print_error("Bad --stp-root-addr address");
+		break;
+	case EBT_STP_SENDERADDR:
+		if (ebt_get_mac_and_mask(argv[optind-1],
+		    (unsigned char *)stpinfo->config.sender_addr,
+		    (unsigned char *)stpinfo->config.sender_addrmsk))
+			ebt_print_error("Bad --stp-sender-addr address");
+		break;
+	default:
+		ebt_print_error("stp match: this shouldn't happen");
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+	uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+	if (memcmp(entry->destmac, bridge_ula, 6) ||
+	    memcmp(entry->destmsk, msk, 6))
+		ebt_print_error("STP matching is only valid when the "
+				"destination MAC address is the bridge group "
+				"address (BGA) 01:80:c2:00:00:00");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data;
+	struct ebt_stp_config_info *c = &(stpinfo->config);
+	int i;
+
+	for (i = 0; i < STP_NUMOPS; i++) {
+		if (!(stpinfo->bitmask & (1 << i)))
+			continue;
+		printf("--%s %s", opts[i].name,
+		       (stpinfo->invflags & (1 << i)) ? "! " : "");
+		if (EBT_STP_TYPE == (1 << i)) {
+			if (stpinfo->type == BPDU_TYPE_CONFIG)
+				printf("%s", BPDU_TYPE_CONFIG_STRING);
+			else if (stpinfo->type == BPDU_TYPE_TCN)
+				printf("%s", BPDU_TYPE_TCN_STRING);
+			else
+				printf("%d", stpinfo->type);
+		} else if (EBT_STP_FLAGS == (1 << i)) {
+			if (c->flags == FLAG_TC)
+				printf("%s", FLAG_TC_STRING);
+			else if (c->flags == FLAG_TC_ACK)
+				printf("%s", FLAG_TC_ACK_STRING);
+			else
+				printf("%d", c->flags);
+		} else if (EBT_STP_ROOTPRIO == (1 << i))
+			print_range(c->root_priol, c->root_priou);
+		else if (EBT_STP_ROOTADDR == (1 << i))
+			ebt_print_mac_and_mask((unsigned char *)c->root_addr,
+			   (unsigned char*)c->root_addrmsk);
+		else if (EBT_STP_ROOTCOST == (1 << i))
+			print_range(c->root_costl, c->root_costu);
+		else if (EBT_STP_SENDERPRIO == (1 << i))
+			print_range(c->sender_priol, c->sender_priou);
+		else if (EBT_STP_SENDERADDR == (1 << i))
+			ebt_print_mac_and_mask((unsigned char *)c->sender_addr,
+			   (unsigned char *)c->sender_addrmsk);
+		else if (EBT_STP_PORT == (1 << i))
+			print_range(c->portl, c->portu);
+		else if (EBT_STP_MSGAGE == (1 << i))
+			print_range(c->msg_agel, c->msg_ageu);
+		else if (EBT_STP_MAXAGE == (1 << i))
+			print_range(c->max_agel, c->max_ageu);
+		else if (EBT_STP_HELLOTIME == (1 << i))
+			print_range(c->hello_timel, c->hello_timeu);
+		else if (EBT_STP_FWDD == (1 << i))
+			print_range(c->forward_delayl, c->forward_delayu);
+		printf(" ");
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	return (!memcmp(m1->data, m2->data, sizeof(struct ebt_stp_info)));
+}
+
+static struct ebt_u_match stp_match =
+{
+	.name		= "stp",
+	.size		= sizeof(struct ebt_stp_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&stp_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_ulog.c b/userspace/ebtables2/extensions/ebt_ulog.c
new file mode 100644
index 0000000..162586d
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_ulog.c
@@ -0,0 +1,186 @@
+/* ebt_ulog
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * November, 2004
+ */
+
+#define __need_time_t
+#define __need_suseconds_t
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <sys/time.h>
+#include <linux/netfilter_bridge/ebt_ulog.h>
+
+#define CP_NO_LIMIT_S "default_cprange"
+#define CP_NO_LIMIT_N 0
+
+#define ULOG_PREFIX     '1'
+#define ULOG_NLGROUP    '2'
+#define ULOG_CPRANGE    '3'
+#define ULOG_QTHRESHOLD '4'
+#define ULOG_ULOG       '5'
+static struct option opts[] =
+{
+	{ "ulog-prefix"    , required_argument, 0, ULOG_PREFIX     },
+	{ "ulog-nlgroup"   , required_argument, 0, ULOG_NLGROUP    },
+	{ "ulog-cprange"   , required_argument, 0, ULOG_CPRANGE    },
+	{ "ulog-qthreshold", required_argument, 0, ULOG_QTHRESHOLD },
+	{ "ulog"           , no_argument      , 0, ULOG_ULOG       },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"ulog options:\n"
+"--ulog                : use the default ulog parameters\n"
+"--ulog-prefix prefix  : max %d characters (default is no prefix)\n"
+"--ulog-nlgroup group  : 0 < group number < %d (default = %d)\n"
+"--ulog-cprange range  : max copy range (default is " CP_NO_LIMIT_S ")\n"
+"--ulog-qthreshold     : 0 < queueing threshold < %d (default = %d)\n",
+	EBT_ULOG_PREFIX_LEN - 1, EBT_ULOG_MAXNLGROUPS + 1,
+	EBT_ULOG_DEFAULT_NLGROUP + 1, EBT_ULOG_MAX_QLEN + 1,
+	EBT_ULOG_DEFAULT_QTHRESHOLD);
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data;
+
+	uloginfo->prefix[0] = '\0';
+	uloginfo->nlgroup = EBT_ULOG_DEFAULT_NLGROUP;
+	uloginfo->cprange = CP_NO_LIMIT_N; /* Use default netlink buffer size */
+	uloginfo->qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD;
+}
+
+#define OPT_PREFIX     0x01
+#define OPT_NLGROUP    0x02
+#define OPT_CPRANGE    0x04
+#define OPT_QTHRESHOLD 0x08
+#define OPT_ULOG       0x10
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_watcher **watcher)
+{
+	struct ebt_ulog_info *uloginfo;
+	unsigned int i;
+	char *end;
+
+	uloginfo = (struct ebt_ulog_info *)(*watcher)->data;
+	switch (c) {
+	case ULOG_PREFIX:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_PREFIX);
+		if (strlen(optarg) > EBT_ULOG_PREFIX_LEN - 1)
+			ebt_print_error("Prefix too long for ulog-prefix");
+		strcpy(uloginfo->prefix, optarg);
+		break;
+
+	case ULOG_NLGROUP:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_NLGROUP);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("Problem with ulog-nlgroup: %s", optarg);
+		if (i < 1 || i > EBT_ULOG_MAXNLGROUPS)
+			ebt_print_error2("the ulog-nlgroup number must be between 1 and 32");
+		uloginfo->nlgroup = i - 1;
+		break;
+
+	case ULOG_CPRANGE:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_CPRANGE);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			if (strcasecmp(optarg, CP_NO_LIMIT_S))
+				ebt_print_error2("Problem with ulog-cprange: %s", optarg);
+			i = CP_NO_LIMIT_N;
+		}
+		uloginfo->cprange = i;
+		break;
+
+	case ULOG_QTHRESHOLD:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_QTHRESHOLD);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("Problem with ulog-qthreshold: %s", optarg);
+		if (i > EBT_ULOG_MAX_QLEN)
+			ebt_print_error2("ulog-qthreshold argument %d exceeds the maximum of %d", i, EBT_ULOG_MAX_QLEN);
+		uloginfo->qthreshold = i;
+		break;
+	case ULOG_ULOG:
+		if (ebt_check_inverse(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_ULOG);
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+
+inverse_invalid:
+	ebt_print_error("The use of '!' makes no sense for the ulog watcher");
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data;
+
+	printf("--ulog-prefix \"%s\" --ulog-nlgroup %d --ulog-cprange ",
+	       uloginfo->prefix, uloginfo->nlgroup + 1);
+	if (uloginfo->cprange == CP_NO_LIMIT_N)
+		printf(CP_NO_LIMIT_S);
+	else
+		printf("%d", uloginfo->cprange);
+	printf(" --ulog-qthreshold %d ", uloginfo->qthreshold);
+}
+
+static int compare(const struct ebt_entry_watcher *w1,
+   const struct ebt_entry_watcher *w2)
+{
+	struct ebt_ulog_info *uloginfo1 = (struct ebt_ulog_info *)w1->data;
+	struct ebt_ulog_info *uloginfo2 = (struct ebt_ulog_info *)w2->data;
+
+	if (uloginfo1->nlgroup != uloginfo2->nlgroup ||
+	    uloginfo1->cprange != uloginfo2->cprange ||
+	    uloginfo1->qthreshold != uloginfo2->qthreshold ||
+	    strcmp(uloginfo1->prefix, uloginfo2->prefix))
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_watcher ulog_watcher =
+{
+	.name		= "ulog",
+	.size		= sizeof(struct ebt_ulog_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_watcher(&ulog_watcher);
+}
diff --git a/userspace/ebtables2/extensions/ebt_vlan.c b/userspace/ebtables2/extensions/ebt_vlan.c
new file mode 100644
index 0000000..6714c82
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_vlan.c
@@ -0,0 +1,187 @@
+/* ebt_vlan
+ * 
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Nick Fedchik <nick@fedchik.org.ua> 
+ *
+ * June, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_vlan.h>
+#include <linux/if_ether.h>
+
+#define NAME_VLAN_ID    "id"
+#define NAME_VLAN_PRIO  "prio"
+#define NAME_VLAN_ENCAP "encap"
+
+#define VLAN_ID    '1'
+#define VLAN_PRIO  '2'
+#define VLAN_ENCAP '3'
+
+static struct option opts[] = {
+	{"vlan-id"   , required_argument, NULL, VLAN_ID},
+	{"vlan-prio" , required_argument, NULL, VLAN_PRIO},
+	{"vlan-encap", required_argument, NULL, VLAN_ENCAP},
+	{ 0 }
+};
+
+/*
+ * option inverse flags definition 
+ */
+#define OPT_VLAN_ID     0x01
+#define OPT_VLAN_PRIO   0x02
+#define OPT_VLAN_ENCAP  0x04
+#define OPT_VLAN_FLAGS	(OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP)
+
+struct ethertypeent *ethent;
+
+static void print_help()
+{
+	printf(
+"vlan options:\n"
+"--vlan-id [!] id       : vlan-tagged frame identifier, 0,1-4096 (integer)\n"
+"--vlan-prio [!] prio   : Priority-tagged frame's user priority, 0-7 (integer)\n"
+"--vlan-encap [!] encap : Encapsulated frame protocol (hexadecimal or name)\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data;
+	vlaninfo->invflags = 0;
+	vlaninfo->bitmask = 0;
+}
+
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) (*match)->data;
+	char *end;
+	struct ebt_vlan_info local;
+
+	switch (c) {
+	case VLAN_ID:
+		ebt_check_option2(flags, OPT_VLAN_ID);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_ID;
+		local.id = strtoul(optarg, &end, 10);
+		if (local.id > 4094 || *end != '\0')
+			ebt_print_error2("Invalid --vlan-id range ('%s')", optarg);
+		vlaninfo->id = local.id;
+		vlaninfo->bitmask |= EBT_VLAN_ID;
+		break;
+	case VLAN_PRIO:
+		ebt_check_option2(flags, OPT_VLAN_PRIO);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_PRIO;
+		local.prio = strtoul(optarg, &end, 10);
+		if (local.prio >= 8 || *end != '\0')
+			ebt_print_error2("Invalid --vlan-prio range ('%s')", optarg);
+		vlaninfo->prio = local.prio;
+		vlaninfo->bitmask |= EBT_VLAN_PRIO;
+		break;
+	case VLAN_ENCAP:
+		ebt_check_option2(flags, OPT_VLAN_ENCAP);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_ENCAP;
+		local.encap = strtoul(optarg, &end, 16);
+		if (*end != '\0') {
+			ethent = getethertypebyname(optarg);
+			if (ethent == NULL)
+				ebt_print_error("Unknown --vlan-encap value ('%s')", optarg);
+			local.encap = ethent->e_ethertype;
+		}
+		if (local.encap < ETH_ZLEN)
+			ebt_print_error2("Invalid --vlan-encap range ('%s')", optarg);
+		vlaninfo->encap = htons(local.encap);
+		vlaninfo->bitmask |= EBT_VLAN_ENCAP;
+		break;
+	default:
+		return 0;
+
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match,
+   const char *name, unsigned int hookmask, unsigned int time)
+{
+	if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO)
+		ebt_print_error("For vlan filtering the protocol must be specified as 802_1Q");
+
+	/* Check if specified vlan-id=0 (priority-tagged frame condition) 
+	 * when vlan-prio was specified. */
+	/* I see no reason why a user should be prohibited to match on a perhaps impossible situation <BDS>
+	if (vlaninfo->bitmask & EBT_VLAN_PRIO &&
+	    vlaninfo->id && vlaninfo->bitmask & EBT_VLAN_ID)
+		ebt_print_error("When setting --vlan-prio the specified --vlan-id must be 0");*/
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data;
+
+	if (vlaninfo->bitmask & EBT_VLAN_ID) {
+		printf("--vlan-id %s%d ", (vlaninfo->invflags & EBT_VLAN_ID) ? "! " : "", vlaninfo->id);
+	}
+	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+		printf("--vlan-prio %s%d ", (vlaninfo->invflags & EBT_VLAN_PRIO) ? "! " : "", vlaninfo->prio);
+	}
+	if (vlaninfo->bitmask & EBT_VLAN_ENCAP) {
+		printf("--vlan-encap %s", (vlaninfo->invflags & EBT_VLAN_ENCAP) ? "! " : "");
+		ethent = getethertypebynumber(ntohs(vlaninfo->encap));
+		if (ethent != NULL) {
+			printf("%s ", ethent->e_name);
+		} else {
+			printf("%4.4X ", ntohs(vlaninfo->encap));
+		}
+	}
+}
+
+static int compare(const struct ebt_entry_match *vlan1,
+   const struct ebt_entry_match *vlan2)
+{
+	struct ebt_vlan_info *vlaninfo1 = (struct ebt_vlan_info *) vlan1->data;
+	struct ebt_vlan_info *vlaninfo2 = (struct ebt_vlan_info *) vlan2->data;
+
+	if (vlaninfo1->bitmask != vlaninfo2->bitmask)
+		return 0;
+	if (vlaninfo1->invflags != vlaninfo2->invflags)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_ID &&
+	    vlaninfo1->id != vlaninfo2->id)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_PRIO &&
+	    vlaninfo1->prio != vlaninfo2->prio)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_ENCAP &&
+	    vlaninfo1->encap != vlaninfo2->encap)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match vlan_match = {
+	.name		= "vlan",
+	.size		= sizeof(struct ebt_vlan_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+void _init(void)
+{
+	ebt_register_match(&vlan_match);
+}
diff --git a/userspace/ebtables2/extensions/ebtable_broute.c b/userspace/ebtables2/extensions/ebtable_broute.c
new file mode 100644
index 0000000..5259355
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_broute.c
@@ -0,0 +1,29 @@
+/* ebtable_broute
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+
+static void print_help(const char **hn)
+{
+	printf("Supported chain for the broute table:\n");
+	printf("%s\n",hn[NF_BR_BROUTING]);
+}
+
+static struct
+ebt_u_table table =
+{
+	.name		= "broute",
+	.help		= print_help,
+};
+
+void _init(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/userspace/ebtables2/extensions/ebtable_filter.c b/userspace/ebtables2/extensions/ebtable_filter.c
new file mode 100644
index 0000000..e41fb84
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_filter.c
@@ -0,0 +1,35 @@
+/* ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+   (1 << NF_BR_LOCAL_OUT))
+
+static void print_help(const char **hn)
+{
+	int i;
+
+	printf("Supported chains for the filter table:\n");
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (FILTER_VALID_HOOKS & (1 << i))
+			printf("%s ", hn[i]);
+	printf("\n");
+}
+
+static struct ebt_u_table table =
+{
+	.name		= "filter",
+	.help		= print_help,
+};
+
+void _init(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/userspace/ebtables2/extensions/ebtable_nat.c b/userspace/ebtables2/extensions/ebtable_nat.c
new file mode 100644
index 0000000..b21c9dd
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_nat.c
@@ -0,0 +1,36 @@
+/* ebtable_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+   (1 << NF_BR_POST_ROUTING))
+
+static void print_help(const char **hn)
+{
+	int i;
+
+	printf("Supported chains for the nat table:\n");
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (NAT_VALID_HOOKS & (1 << i))
+			printf("%s ", hn[i]);
+	printf("\n");
+}
+
+static struct
+ebt_u_table table =
+{
+	.name		= "nat",
+	.help		= print_help,
+};
+
+void _init(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/userspace/ebtables2/getethertype.c b/userspace/ebtables2/getethertype.c
new file mode 100644
index 0000000..e4e4812
--- /dev/null
+++ b/userspace/ebtables2/getethertype.c
@@ -0,0 +1,161 @@
+/*
+* getethertype.c
+*
+* This file was part of the NYS Library.
+*
+** The NYS Library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library General Public License as
+** published by the Free Software Foundation; either version 2 of the
+** License, or (at your option) any later version.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/********************************************************************
+* Description: Ethertype name service switch and the ethertypes 
+* database access functions
+* Author: Nick Fedchik <fnm@ukrsat.com>
+* Checker: Bart De Schuymer <bdschuym@pandora.be>
+* Origin: uClibc-0.9.16/libc/inet/getproto.c
+* Created at: Mon Nov 11 12:20:11 EET 2002
+********************************************************************/
+
+#include <ctype.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+
+#include "ethernetdb.h"
+
+#define	MAXALIASES	35
+
+static FILE *etherf = NULL;
+static char line[BUFSIZ + 1];
+static struct ethertypeent et_ent;
+static char *ethertype_aliases[MAXALIASES];
+static int ethertype_stayopen;
+
+void setethertypeent(int f)
+{
+	if (etherf == NULL)
+		etherf = fopen(_PATH_ETHERTYPES, "r");
+	else
+		rewind(etherf);
+	ethertype_stayopen |= f;
+}
+
+void endethertypeent(void)
+{
+	if (etherf) {
+		fclose(etherf);
+		etherf = NULL;
+	}
+	ethertype_stayopen = 0;
+}
+
+struct ethertypeent *getethertypeent(void)
+{
+	char *e;
+	char *endptr;
+	register char *cp, **q;
+
+	if (etherf == NULL
+	    && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
+		return (NULL);
+	}
+
+again:
+	if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
+		return (NULL);
+	}
+	if (*e == '#')
+		goto again;
+	cp = strpbrk(e, "#\n");
+	if (cp == NULL)
+		goto again;
+	*cp = '\0';
+	et_ent.e_name = e;
+	cp = strpbrk(e, " \t");
+	if (cp == NULL)
+		goto again;
+	*cp++ = '\0';
+	while (*cp == ' ' || *cp == '\t')
+		cp++;
+	e = strpbrk(cp, " \t");
+	if (e != NULL)
+		*e++ = '\0';
+// Check point
+	et_ent.e_ethertype = strtol(cp, &endptr, 16);
+	if (*endptr != '\0'
+	    || (et_ent.e_ethertype < ETH_ZLEN
+		|| et_ent.e_ethertype > 0xFFFF))
+		goto again;	// Skip invalid etherproto type entry
+	q = et_ent.e_aliases = ethertype_aliases;
+	if (e != NULL) {
+		cp = e;
+		while (cp && *cp) {
+			if (*cp == ' ' || *cp == '\t') {
+				cp++;
+				continue;
+			}
+			if (q < &ethertype_aliases[MAXALIASES - 1])
+				*q++ = cp;
+			cp = strpbrk(cp, " \t");
+			if (cp != NULL)
+				*cp++ = '\0';
+		}
+	}
+	*q = NULL;
+	return (&et_ent);
+}
+
+
+struct ethertypeent *getethertypebyname(const char *name)
+{
+	register struct ethertypeent *e;
+	register char **cp;
+
+	setethertypeent(ethertype_stayopen);
+	while ((e = getethertypeent()) != NULL) {
+		if (strcasecmp(e->e_name, name) == 0)
+			break;
+		for (cp = e->e_aliases; *cp != 0; cp++)
+			if (strcasecmp(*cp, name) == 0)
+				goto found;
+	}
+found:
+	if (!ethertype_stayopen)
+		endethertypeent();
+	return (e);
+}
+
+struct ethertypeent *getethertypebynumber(int type)
+{
+	register struct ethertypeent *e;
+
+	setethertypeent(ethertype_stayopen);
+	while ((e = getethertypeent()) != NULL)
+		if (e->e_ethertype == type)
+			break;
+	if (!ethertype_stayopen)
+		endethertypeent();
+	return (e);
+}
diff --git a/userspace/ebtables2/include/ebtables.h b/userspace/ebtables2/include/ebtables.h
new file mode 100644
index 0000000..8f520c6
--- /dev/null
+++ b/userspace/ebtables2/include/ebtables.h
@@ -0,0 +1,276 @@
+/*
+ *  ebtables
+ *
+ *	Authors:
+ *	Bart De Schuymer		<bdschuym@pandora.be>
+ *
+ *  ebtables.c,v 2.0, April, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+/* Local copy of the kernel file, needed for Sparc64 support */
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+/* verdicts >0 are "branches" */
+#define EBT_ACCEPT   -1
+#define EBT_DROP     -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN   -4
+#define NUM_STANDARD_TARGETS   4
+/* ebtables target modules store the verdict inside an int. We can
+ * reclaim a part of this int for backwards compatible extensions.
+ * The 4 lsb are more than enough to store the verdict. */
+#define EBT_VERDICT_BITS 0x0000000F
+
+struct ebt_counter
+{
+	uint64_t pcnt;
+	uint64_t bcnt;
+};
+
+struct ebt_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	/* total size of the entries */
+	unsigned int entries_size;
+	/* start of the chains */
+#ifdef KERNEL_64_USERSPACE_32
+	uint64_t hook_entry[NF_BR_NUMHOOKS];
+#else
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+#endif
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+#ifdef KERNEL_64_USERSPACE_32
+	uint64_t counters;
+	uint64_t entries;
+#else
+	struct ebt_counter *counters;
+	char *entries;
+#endif
+};
+
+struct ebt_entries {
+	/* this field is always set to zero
+	 * See EBT_ENTRY_OR_ENTRIES.
+	 * Must be same size as ebt_entry.bitmask */
+	unsigned int distinguisher;
+	/* the chain name */
+	char name[EBT_CHAIN_MAXNAMELEN];
+	/* counter offset for this chain */
+	unsigned int counter_offset;
+	/* one standard (accept, drop, return) per hook */
+	int policy;
+	/* nr. of entries */
+	unsigned int nentries;
+	/* entry list */
+	char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* used for the bitmask of struct ebt_entry */
+
+/* This is a hack to make a difference between an ebt_entry struct and an
+ * ebt_entries struct when traversing the entries from start to end.
+ * Using this simplifies the code alot, while still being able to use
+ * ebt_entries.
+ * Contrary, iptables doesn't use something like ebt_entries and therefore uses
+ * different techniques for naming the policy and such. So, iptables doesn't
+ * need a hack like this.
+ */
+#define EBT_ENTRY_OR_ENTRIES 0x01
+/* these are the normal masks */
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+   | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_match *match;
+	} u;
+	/* size of data */
+	unsigned int match_size;
+#ifdef KERNEL_64_USERSPACE_32
+	unsigned int pad;
+#endif
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_watcher
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_watcher *watcher;
+	} u;
+	/* size of data */
+	unsigned int watcher_size;
+#ifdef KERNEL_64_USERSPACE_32
+	unsigned int pad;
+#endif
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_target
+{
+	union {
+		char name[EBT_FUNCTION_MAXNAMELEN];
+		struct ebt_target *target;
+	} u;
+	/* size of data */
+	unsigned int target_size;
+#ifdef KERNEL_64_USERSPACE_32
+	unsigned int pad;
+#endif
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+	struct ebt_entry_target target;
+	int verdict;
+#ifdef KERNEL_64_USERSPACE_32
+	unsigned int pad;
+#endif
+};
+
+/* one entry */
+struct ebt_entry {
+	/* this needs to be the first field */
+	unsigned int bitmask;
+	unsigned int invflags;
+	uint16_t ethproto;
+	/* the physical in-dev */
+	char in[IFNAMSIZ];
+	/* the logical in-dev */
+	char logical_in[IFNAMSIZ];
+	/* the physical out-dev */
+	char out[IFNAMSIZ];
+	/* the logical out-dev */
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	/* sizeof ebt_entry + matches */
+	unsigned int watchers_offset;
+	/* sizeof ebt_entry + matches + watchers */
+	unsigned int target_offset;
+	/* sizeof ebt_entry + matches + watchers + target */
+	unsigned int next_offset;
+	unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* {g,s}etsockopt numbers */
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+/* blatently stolen from ip_tables.h
+ * fn returns 0 to continue iteration */
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_match *__match;                    \
+	                                                    \
+	for (__i = sizeof(struct ebt_entry);                \
+	     __i < (e)->watchers_offset;                    \
+	     __i += __match->match_size +                   \
+	     sizeof(struct ebt_entry_match)) {              \
+		__match = (void *)(e) + __i;                \
+		                                            \
+		__ret = fn(__match , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->watchers_offset)            \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_watcher *__watcher;                \
+	                                                    \
+	for (__i = e->watchers_offset;                      \
+	     __i < (e)->target_offset;                      \
+	     __i += __watcher->watcher_size +               \
+	     sizeof(struct ebt_entry_watcher)) {            \
+		__watcher = (void *)(e) + __i;              \
+		                                            \
+		__ret = fn(__watcher , ## args);            \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->target_offset)              \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry *__entry;                          \
+	                                                    \
+	for (__i = 0; __i < (size);) {                      \
+		__entry = (void *)(entries) + __i;          \
+		__ret = fn(__entry , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+		if (__entry->bitmask != 0)                  \
+			__i += __entry->next_offset;        \
+		else                                        \
+			__i += sizeof(struct ebt_entries);  \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (size))                          \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#endif
diff --git a/userspace/ebtables2/include/ebtables_u.h b/userspace/ebtables2/include/ebtables_u.h
new file mode 100644
index 0000000..ab615c1
--- /dev/null
+++ b/userspace/ebtables2/include/ebtables_u.h
@@ -0,0 +1,385 @@
+/*
+ * $Id: ebtables.c,v 1.03 2002/01/19
+ *
+ * Copyright (C) 2001-2002 Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef EBTABLES_U_H
+#define EBTABLES_U_H
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter/x_tables.h>
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP		132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP		33
+#endif
+
+#define EXEC_STYLE_PRG		0
+#define EXEC_STYLE_DAEMON	1
+
+#ifndef EBT_MIN_ALIGN
+#define EBT_MIN_ALIGN (__alignof__(struct _xt_align))
+#endif
+#define EBT_ALIGN(s) (((s) + (EBT_MIN_ALIGN-1)) & ~(EBT_MIN_ALIGN-1))
+#define ERRORMSG_MAXLEN 128
+
+struct ebt_u_entries
+{
+	int policy;
+	unsigned int nentries;
+	/* counter offset for this chain */
+	unsigned int counter_offset;
+	/* used for udc */
+	unsigned int hook_mask;
+	char *kernel_start;
+	char name[EBT_CHAIN_MAXNAMELEN];
+	struct ebt_u_entry *entries;
+};
+
+struct ebt_cntchanges
+{
+	unsigned short type;
+	unsigned short change; /* determines incremental/decremental/change */
+	struct ebt_cntchanges *prev;
+	struct ebt_cntchanges *next;
+};
+
+#define EBT_ORI_MAX_CHAINS 10
+struct ebt_u_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	unsigned int num_chains;
+	unsigned int max_chains;
+	struct ebt_u_entries **chains;
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+	struct ebt_counter *counters;
+	/*
+	 * can be used e.g. to know if a standard option
+	 * has been specified twice
+	 */
+	unsigned int flags;
+	/* we stick the specified command (e.g. -A) in here */
+	char command;
+	/*
+	 * here we stick the chain to do our thing on (can be -1 if unspecified)
+	 */
+	int selected_chain;
+	/* used for the atomic option */
+	char *filename;
+	/* tells what happened to the old rules (counter changes) */
+	struct ebt_cntchanges *cc;
+};
+
+struct ebt_u_table
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	void (*check)(struct ebt_u_replace *repl);
+	void (*help)(const char **);
+	struct ebt_u_table *next;
+};
+
+struct ebt_u_match_list
+{
+	struct ebt_u_match_list *next;
+	struct ebt_entry_match *m;
+};
+
+struct ebt_u_watcher_list
+{
+	struct ebt_u_watcher_list *next;
+	struct ebt_entry_watcher *w;
+};
+
+struct ebt_u_entry
+{
+	unsigned int bitmask;
+	unsigned int invflags;
+	uint16_t ethproto;
+	char in[IFNAMSIZ];
+	char logical_in[IFNAMSIZ];
+	char out[IFNAMSIZ];
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	struct ebt_u_match_list *m_list;
+	struct ebt_u_watcher_list *w_list;
+	struct ebt_entry_target *t;
+	struct ebt_u_entry *prev;
+	struct ebt_u_entry *next;
+	struct ebt_counter cnt;
+	struct ebt_counter cnt_surplus; /* for increasing/decreasing a counter and for option 'C' */
+	struct ebt_cntchanges *cc;
+	/* the standard target needs this to know the name of a udc when
+	 * printing out rules. */
+	struct ebt_u_replace *replace;
+};
+
+struct ebt_u_match
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	/* size of the real match data */
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_match *m);
+	int (*parse)(int c, char **argv, int argc,
+	        const struct ebt_u_entry *entry, unsigned int *flags,
+	        struct ebt_entry_match **match);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_match *match,
+	   const char *name, unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_match *match);
+	int (*compare)(const struct ebt_entry_match *m1,
+	   const struct ebt_entry_match *m2);
+	const struct option *extra_ops;
+	/*
+	 * can be used e.g. to check for multiple occurance of the same option
+	 */
+	unsigned int flags;
+	unsigned int option_offset;
+	struct ebt_entry_match *m;
+	/*
+	 * if used == 1 we no longer have to add it to
+	 * the match chain of the new entry
+	 * be sure to put it back on 0 when finished
+	 */
+	unsigned int used;
+	struct ebt_u_match *next;
+};
+
+struct ebt_u_watcher
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_watcher *w);
+	int (*parse)(int c, char **argv, int argc,
+	   const struct ebt_u_entry *entry, unsigned int *flags,
+	   struct ebt_entry_watcher **watcher);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_watcher *watch, const char *name,
+	   unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_watcher *watcher);
+	int (*compare)(const struct ebt_entry_watcher *w1,
+	   const struct ebt_entry_watcher *w2);
+	const struct option *extra_ops;
+	unsigned int flags;
+	unsigned int option_offset;
+	struct ebt_entry_watcher *w;
+	unsigned int used;
+	struct ebt_u_watcher *next;
+};
+
+struct ebt_u_target
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_target *t);
+	int (*parse)(int c, char **argv, int argc,
+	   const struct ebt_u_entry *entry, unsigned int *flags,
+	   struct ebt_entry_target **target);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_target *target, const char *name,
+	   unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_target *target);
+	int (*compare)(const struct ebt_entry_target *t1,
+	   const struct ebt_entry_target *t2);
+	const struct option *extra_ops;
+	unsigned int option_offset;
+	unsigned int flags;
+	struct ebt_entry_target *t;
+	unsigned int used;
+	struct ebt_u_target *next;
+};
+
+/* libebtc.c */
+
+extern struct ebt_u_table *ebt_tables;
+extern struct ebt_u_match *ebt_matches;
+extern struct ebt_u_watcher *ebt_watchers;
+extern struct ebt_u_target *ebt_targets;
+
+extern int use_lockfd;
+
+void ebt_register_table(struct ebt_u_table *);
+void ebt_register_match(struct ebt_u_match *);
+void ebt_register_watcher(struct ebt_u_watcher *);
+void ebt_register_target(struct ebt_u_target *t);
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init);
+struct ebt_u_target *ebt_find_target(const char *name);
+struct ebt_u_match *ebt_find_match(const char *name);
+struct ebt_u_watcher *ebt_find_watcher(const char *name);
+struct ebt_u_table *ebt_find_table(const char *name);
+int ebtables_insmod(const char *modname);
+void ebt_list_extensions();
+void ebt_initialize_entry(struct ebt_u_entry *e);
+void ebt_cleanup_replace(struct ebt_u_replace *replace);
+void ebt_reinit_extensions();
+void ebt_double_chains(struct ebt_u_replace *replace);
+void ebt_free_u_entry(struct ebt_u_entry *e);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+				    const char* arg);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+				    const char* arg);
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg);
+/**/
+void ebt_change_policy(struct ebt_u_replace *replace, int policy);
+void ebt_flush_chains(struct ebt_u_replace *replace);
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+			  struct ebt_u_entry *new_entry);
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry,
+		  int rule_nr);
+void ebt_delete_rule(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end);
+void ebt_zero_counters(struct ebt_u_replace *replace);
+void ebt_change_counters(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end,
+		     struct ebt_counter *cnt, int mask);
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy);
+void ebt_delete_chain(struct ebt_u_replace *replace);
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name);
+/**/
+void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
+			 struct ebt_u_entries *entries);
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err);
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+			      int print_err);
+void ebt_check_for_loops(struct ebt_u_replace *replace);
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m);
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w);
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *));
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *));
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *));
+void __ebt_print_bug(char *file, int line, char *format, ...);
+void __ebt_print_error(char *format, ...);
+
+/* communication.c */
+
+int ebt_get_table(struct ebt_u_replace *repl, int init);
+void ebt_deliver_counters(struct ebt_u_replace *repl);
+void ebt_deliver_table(struct ebt_u_replace *repl);
+
+/* useful_functions.c */
+
+extern int ebt_invert;
+void ebt_check_option(unsigned int *flags, unsigned int mask);
+#define ebt_check_inverse(arg) _ebt_check_inverse(arg, argc, argv)
+int _ebt_check_inverse(const char option[], int argc, char **argv);
+void ebt_print_mac(const unsigned char *mac);
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask);
+int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+char *ebt_mask_to_dotted(uint32_t mask);
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr, 
+						   struct in6_addr *msk);
+char *ebt_ip6_to_numeric(const struct in6_addr *addrp);
+
+
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_);
+
+struct ethertypeent *parseethertypebynumber(int type);
+
+#define ebt_to_chain(repl)				\
+({struct ebt_u_entries *_ch = NULL;			\
+if (repl->selected_chain != -1)				\
+	_ch = repl->chains[repl->selected_chain];	\
+_ch;})
+#define ebt_print_bug(format, args...) \
+   __ebt_print_bug(__FILE__, __LINE__, format, ##args)
+#define ebt_print_error(format,args...) __ebt_print_error(format, ##args);
+#define ebt_print_error2(format, args...) do {__ebt_print_error(format, ##args); \
+   return -1;} while (0)
+#define ebt_check_option2(flags,mask)	\
+({ebt_check_option(flags,mask);		\
+ if (ebt_errormsg[0] != '\0')		\
+	return -1;})
+#define ebt_check_inverse2(option)					\
+({int __ret = ebt_check_inverse(option);				\
+if (ebt_errormsg[0] != '\0')						\
+	return -1;							\
+if (!optarg) {								\
+	__ebt_print_error("Option without (mandatory) argument");	\
+	return -1;							\
+}									\
+__ret;})
+#define ebt_print_memory() do {printf("Ebtables: " __FILE__ \
+   " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);} while (0)
+
+/* used for keeping the rule counters right during rule adds or deletes */
+#define CNT_NORM 	0
+#define CNT_DEL 	1
+#define CNT_ADD 	2
+#define CNT_CHANGE 	3
+
+extern const char *ebt_hooknames[NF_BR_NUMHOOKS];
+extern const char *ebt_standard_targets[NUM_STANDARD_TARGETS];
+extern char ebt_errormsg[ERRORMSG_MAXLEN];
+extern char *ebt_modprobe;
+extern int ebt_silent;
+extern int ebt_printstyle_mac;
+
+/*
+ * Transforms a target string into the right integer,
+ * returns 0 on success.
+ */
+#define FILL_TARGET(_str, _pos) ({                            \
+	int _i, _ret = 0;                                     \
+	for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++)         \
+		if (!strcmp(_str, ebt_standard_targets[_i])) {\
+			_pos = -_i - 1;                       \
+			break;                                \
+		}                                             \
+	if (_i == NUM_STANDARD_TARGETS)                       \
+		_ret = 1;                                     \
+	_ret;                                                 \
+})
+
+/* Transforms the target value to an index into standard_targets[] */
+#define TARGET_INDEX(_value) (-_value - 1)
+/* Returns a target string corresponding to the value */
+#define TARGET_NAME(_value) (ebt_standard_targets[TARGET_INDEX(_value)])
+/* True if the hook mask denotes that the rule is in a base chain */
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+/* Clear the bit in the hook_mask that tells if the rule is on a base chain */
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+#define PRINT_VERSION printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n")
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
+#endif
+#endif /* EBTABLES_U_H */
diff --git a/userspace/ebtables2/include/ethernetdb.h b/userspace/ebtables2/include/ethernetdb.h
new file mode 100644
index 0000000..46d8bfd
--- /dev/null
+++ b/userspace/ebtables2/include/ethernetdb.h
@@ -0,0 +1,58 @@
+/*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* All data returned by the network data base library are supplied in
+   host order and returned in network order (suitable for use in
+   system calls).  */
+
+#ifndef	_ETHERNETDB_H
+#define	_ETHERNETDB_H	1
+
+#include <features.h>
+#include <netinet/in.h>
+#include <stdint.h>
+
+/* Absolute file name for network data base files.  */
+#ifndef	_PATH_ETHERTYPES
+#define	_PATH_ETHERTYPES	"/etc/ethertypes"
+#endif				/* _PATH_ETHERTYPES */
+
+struct ethertypeent {
+	char *e_name;		/* Official ethernet type name.  */
+	char **e_aliases;	/* Alias list.  */
+	int e_ethertype;	/* Ethernet type number.  */
+};
+
+/* Open ethertype data base files and mark them as staying open even
+   after a later search if STAY_OPEN is non-zero.  */
+extern void setethertypeent(int __stay_open) __THROW;
+
+/* Close ethertype data base files and clear `stay open' flag.  */
+extern void endethertypeent(void) __THROW;
+
+/* Get next entry from ethertype data base file.  Open data base if
+   necessary.  */
+extern struct ethertypeent *getethertypeent(void) __THROW;
+
+/* Return entry from ethertype data base for network with NAME.  */
+extern struct ethertypeent *getethertypebyname(__const char *__name)
+    __THROW;
+
+/* Return entry from ethertype data base which number is PROTO.  */
+extern struct ethertypeent *getethertypebynumber(int __ethertype) __THROW;
+
+
+#endif				/* ethernetdb.h */
diff --git a/userspace/ebtables2/libebtc.c b/userspace/ebtables2/libebtc.c
new file mode 100644
index 0000000..17ba8f2
--- /dev/null
+++ b/userspace/ebtables2/libebtc.c
@@ -0,0 +1,1351 @@
+/*
+ * libebtc.c, January 2004
+ *
+ * Contains the functions with which to make a table in userspace.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace);
+static int iterate_entries(struct ebt_u_replace *replace, int type);
+
+/* The standard names */
+const char *ebt_hooknames[NF_BR_NUMHOOKS] =
+{
+	[NF_BR_PRE_ROUTING]"PREROUTING",
+	[NF_BR_LOCAL_IN]"INPUT",
+	[NF_BR_FORWARD]"FORWARD",
+	[NF_BR_LOCAL_OUT]"OUTPUT",
+	[NF_BR_POST_ROUTING]"POSTROUTING",
+	[NF_BR_BROUTING]"BROUTING"
+};
+
+/* The four target names */
+const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
+{
+	"ACCEPT",
+	"DROP",
+	"CONTINUE",
+	"RETURN",
+};
+
+/* The lists of supported tables, matches, watchers and targets */
+struct ebt_u_table *ebt_tables;
+struct ebt_u_match *ebt_matches;
+struct ebt_u_watcher *ebt_watchers;
+struct ebt_u_target *ebt_targets;
+
+/* Find the right structure belonging to a name */
+struct ebt_u_target *ebt_find_target(const char *name)
+{
+	struct ebt_u_target *t = ebt_targets;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+struct ebt_u_match *ebt_find_match(const char *name)
+{
+	struct ebt_u_match *m = ebt_matches;
+
+	while (m && strcmp(m->name, name))
+		m = m->next;
+	return m;
+}
+
+struct ebt_u_watcher *ebt_find_watcher(const char *name)
+{
+	struct ebt_u_watcher *w = ebt_watchers;
+
+	while (w && strcmp(w->name, name))
+		w = w->next;
+	return w;
+}
+
+struct ebt_u_table *ebt_find_table(const char *name)
+{
+	struct ebt_u_table *t = ebt_tables;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+/* Prints all registered extensions */
+void ebt_list_extensions()
+{
+	struct ebt_u_table *tbl = ebt_tables;
+        struct ebt_u_target *t = ebt_targets;
+        struct ebt_u_match *m = ebt_matches;
+        struct ebt_u_watcher *w = ebt_watchers;
+
+	PRINT_VERSION;
+	printf("Loaded userspace extensions:\n\nLoaded tables:\n");
+        while (tbl) {
+		printf("%s\n", tbl->name);
+                tbl = tbl->next;
+	}
+	printf("\nLoaded targets:\n");
+        while (t) {
+		printf("%s\n", t->name);
+                t = t->next;
+	}
+	printf("\nLoaded matches:\n");
+        while (m) {
+		printf("%s\n", m->name);
+                m = m->next;
+	}
+	printf("\nLoaded watchers:\n");
+        while (w) {
+		printf("%s\n", w->name);
+                w = w->next;
+	}
+}
+
+#ifndef LOCKFILE
+#define LOCKDIR "/var/lib/ebtables"
+#define LOCKFILE LOCKDIR"/lock"
+#endif
+static int lockfd = -1, locked;
+int use_lockfd;
+/* Returns 0 on success, -1 when the file is locked by another process
+ * or -2 on any other error. */
+static int lock_file()
+{
+	int try = 0;
+	int ret = 0;
+	sigset_t sigset;
+
+tryagain:
+	/* the SIGINT handler will call unlock_file. To make sure the state
+	 * of the variable locked is correct, we need to temporarily mask the
+	 * SIGINT interrupt. */
+	sigemptyset(&sigset);
+	sigaddset(&sigset, SIGINT);
+	sigprocmask(SIG_BLOCK, &sigset, NULL);
+	lockfd = open(LOCKFILE, O_CREAT | O_EXCL | O_WRONLY, 00600);
+	if (lockfd < 0) {
+		if (errno == EEXIST)
+			ret = -1;
+		else if (try == 1)
+			ret = -2;
+		else {
+			if (mkdir(LOCKDIR, 00700))
+				ret = -2;
+			else {
+				try = 1;
+				goto tryagain;
+			}
+		}
+	} else {
+		close(lockfd);
+		locked = 1;
+	}
+	sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+	return ret;
+}
+
+void unlock_file()
+{
+	if (locked) {
+		remove(LOCKFILE);
+		locked = 0;
+	}
+}
+
+void __attribute__ ((destructor)) onexit()
+{
+	if (use_lockfd)
+		unlock_file();
+}
+/* Get the table from the kernel or from a binary file
+ * init: 1 = ask the kernel for the initial contents of a table, i.e. the
+ *           way it looks when the table is insmod'ed
+ *       0 = get the current data in the table */
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
+{
+	int ret;
+
+	if (!ebt_find_table(replace->name)) {
+		ebt_print_error("Bad table name '%s'", replace->name);
+		return -1;
+	}
+	while (use_lockfd && (ret = lock_file())) {
+		if (ret == -2) {
+			/* if we get an error we can't handle, we exit. This
+			 * doesn't break backwards compatibility since using
+			 * this file locking is disabled by default. */
+			ebt_print_error2("Unable to create lock file "LOCKFILE);
+		}
+		fprintf(stderr, "Trying to obtain lock %s\n", LOCKFILE);
+		sleep(1);
+	}
+	/* Get the kernel's information */
+	if (ebt_get_table(replace, init)) {
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		ebtables_insmod("ebtables");
+		if (ebt_get_table(replace, init)) {
+			ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Put sane values into a new entry */
+void ebt_initialize_entry(struct ebt_u_entry *e)
+{
+	e->bitmask = EBT_NOPROTO;
+	e->invflags = 0;
+	e->ethproto = 0;
+	strcpy(e->in, "");
+	strcpy(e->out, "");
+	strcpy(e->logical_in, "");
+	strcpy(e->logical_out, "");
+	e->m_list = NULL;
+	e->w_list = NULL;
+	e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET);
+	ebt_find_target(EBT_STANDARD_TARGET)->used = 1;
+	e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0;
+
+	if (!e->t)
+		ebt_print_bug("Couldn't load standard target");
+	((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE;
+}
+
+/* Free up the memory of the table held in userspace, *replace can be reused */
+void ebt_cleanup_replace(struct ebt_u_replace *replace)
+{
+	int i;
+	struct ebt_u_entries *entries;
+	struct ebt_cntchanges *cc1, *cc2;
+	struct ebt_u_entry *u_e1, *u_e2;
+
+	replace->name[0] = '\0';
+	replace->valid_hooks = 0;
+	replace->nentries = 0;
+	replace->num_counters = 0;
+	replace->flags = 0;
+	replace->command = 0;
+	replace->selected_chain = -1;
+	free(replace->filename);
+	replace->filename = NULL;
+	free(replace->counters);
+	replace->counters = NULL;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		u_e1 = entries->entries->next;
+		while (u_e1 != entries->entries) {
+			ebt_free_u_entry(u_e1);
+			u_e2 = u_e1->next;
+			free(u_e1);
+			u_e1 = u_e2;
+		}
+		free(entries->entries);
+		free(entries);
+		replace->chains[i] = NULL;
+	}
+	cc1 = replace->cc->next;
+	while (cc1 != replace->cc) {
+		cc2 = cc1->next;
+		free(cc1);
+		cc1 = cc2;
+	}
+	replace->cc->next = replace->cc->prev = replace->cc;
+}
+
+/* Should be called, e.g., between 2 rule adds */
+void ebt_reinit_extensions()
+{
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t;
+	int size;
+
+	/* The init functions should determine by themselves whether they are
+	 * called for the first time or not (when necessary). */
+	for (m = ebt_matches; m; m = m->next) {
+		if (m->used) {
+			size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
+			m->m = (struct ebt_entry_match *)malloc(size);
+			if (!m->m)
+				ebt_print_memory();
+			strcpy(m->m->u.name, m->name);
+			m->m->match_size = EBT_ALIGN(m->size);
+			m->used = 0;
+		}
+		m->flags = 0; /* An error can occur before used is set, while flags is changed. */
+		m->init(m->m);
+	}
+	for (w = ebt_watchers; w; w = w->next) {
+		if (w->used) {
+			size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
+			w->w = (struct ebt_entry_watcher *)malloc(size);
+			if (!w->w)
+				ebt_print_memory();
+			strcpy(w->w->u.name, w->name);
+			w->w->watcher_size = EBT_ALIGN(w->size);
+			w->used = 0;
+		}
+		w->flags = 0;
+		w->init(w->w);
+	}
+	for (t = ebt_targets; t; t = t->next) {
+		if (t->used) {
+			size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
+			t->t = (struct ebt_entry_target *)malloc(size);
+			if (!t->t)
+				ebt_print_memory();
+			strcpy(t->t->u.name, t->name);
+			t->t->target_size = EBT_ALIGN(t->size);
+			t->used = 0;
+		}
+		t->flags = 0;
+		t->init(t->t);
+	}
+}
+
+/* This doesn't free e, because the calling function might need e->next */
+void ebt_free_u_entry(struct ebt_u_entry *e)
+{
+	struct ebt_u_match_list *m_l, *m_l2;
+	struct ebt_u_watcher_list *w_l, *w_l2;
+
+	m_l = e->m_list;
+	while (m_l) {
+		m_l2 = m_l->next;
+		free(m_l->m);
+		free(m_l);
+		m_l = m_l2;
+	}
+	w_l = e->w_list;
+	while (w_l) {
+		w_l2 = w_l->next;
+		free(w_l->w);
+		free(w_l);
+		w_l = w_l2;
+	}
+	free(e->t);
+}
+
+static char *get_modprobe(void)
+{
+	int procfile;
+	char *ret;
+
+	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+	if (procfile < 0)
+		return NULL;
+
+	ret = malloc(1024);
+	if (ret) {
+		if (read(procfile, ret, 1024) == -1)
+			goto fail;
+		/* The kernel adds a '\n' */
+		ret[1023] = '\n';
+		*strchr(ret, '\n') = '\0';
+		close(procfile);
+		return ret;
+	}
+ fail:
+	free(ret);
+	close(procfile);
+	return NULL;
+}
+
+char *ebt_modprobe;
+/* Try to load the kernel module, analogous to ip_tables.c */
+int ebtables_insmod(const char *modname)
+{
+	char *buf = NULL;
+	char *argv[3];
+
+	/* If they don't explicitly set it, read out of /proc */
+	if (!ebt_modprobe) {
+		buf = get_modprobe();
+		if (!buf)
+			return -1;
+		ebt_modprobe = buf; /* Keep the value for possible later use */
+	}
+
+	switch (fork()) {
+	case 0:
+		argv[0] = (char *)ebt_modprobe;
+		argv[1] = (char *)modname;
+		argv[2] = NULL;
+		execv(argv[0], argv);
+
+		/* Not usually reached */
+		exit(0);
+	case -1:
+		return -1;
+
+	default: /* Parent */
+		wait(NULL);
+	}
+
+	return 0;
+}
+
+/* Parse the chain name and return a pointer to the chain base.
+ * Returns NULL on failure. */
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, const char* arg)
+{
+	int i;
+	struct ebt_u_entries *chain;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(chain = replace->chains[i]))
+			continue;
+		if (!strcmp(arg, chain->name))
+			return chain;
+	}
+	return NULL;
+}
+
+/* Parse the chain name and return the corresponding chain nr
+ * returns -1 on failure */
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg)
+{
+	int i;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!replace->chains[i])
+			continue;
+		if (!strcmp(arg, replace->chains[i]->name))
+			return i;
+	}
+	return -1;
+}
+
+     /*
+************
+************
+**COMMANDS**
+************
+************
+     */
+
+/* Change the policy of selected_chain.
+ * Handing a bad policy to this function is a bug. */
+void ebt_change_policy(struct ebt_u_replace *replace, int policy)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE)
+		ebt_print_bug("Wrong policy: %d", policy);
+	entries->policy = policy;
+}
+
+void ebt_delete_cc(struct ebt_cntchanges *cc)
+{
+	if (cc->type == CNT_ADD) {
+		cc->prev->next = cc->next;
+		cc->next->prev = cc->prev;
+		free(cc);
+	} else
+		cc->type = CNT_DEL;
+}
+
+void ebt_empty_chain(struct ebt_u_entries *entries)
+{
+	struct ebt_u_entry *u_e = entries->entries->next, *tmp;
+	while (u_e != entries->entries) {
+		ebt_delete_cc(u_e->cc);
+		ebt_free_u_entry(u_e);
+		tmp = u_e->next;
+		free(u_e);
+		u_e = tmp;
+	}
+	entries->entries->next = entries->entries->prev = entries->entries;
+	entries->nentries = 0;
+}
+
+/* Flush one chain or the complete table
+ * If selected_chain == -1 then flush the complete table */
+void ebt_flush_chains(struct ebt_u_replace *replace)
+{
+	int i, numdel;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	/* Flush whole table */
+	if (!entries) {
+		if (replace->nentries == 0)
+			return;
+		replace->nentries = 0;
+
+		/* Free everything and zero (n)entries */
+		for (i = 0; i < replace->num_chains; i++) {
+			if (!(entries = replace->chains[i]))
+				continue;
+			entries->counter_offset = 0;
+			ebt_empty_chain(entries);
+		}
+		return;
+	}
+
+	if (entries->nentries == 0)
+		return;
+	replace->nentries -= entries->nentries;
+	numdel = entries->nentries;
+
+	/* Update counter_offset */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset -= numdel;
+	}
+
+	entries = ebt_to_chain(replace);
+	ebt_empty_chain(entries);
+}
+
+#define OPT_COUNT	0x1000 /* This value is also defined in ebtables.c */
+/* Returns the rule number on success (starting from 0), -1 on failure
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} */
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+			  struct ebt_u_entry *new_entry)
+{
+	struct ebt_u_entry *u_e;
+	struct ebt_u_match_list *m_l, *m_l2;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher_list *w_l, *w_l2;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	int i, j, k;
+
+	u_e = entries->entries->next;
+	/* Check for an existing rule (if there are duplicate rules,
+	 * take the first occurance) */
+	for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+		if (u_e->ethproto != new_entry->ethproto)
+			continue;
+		if (strcmp(u_e->in, new_entry->in))
+			continue;
+		if (strcmp(u_e->out, new_entry->out))
+			continue;
+		if (strcmp(u_e->logical_in, new_entry->logical_in))
+			continue;
+		if (strcmp(u_e->logical_out, new_entry->logical_out))
+			continue;
+		if (new_entry->bitmask & EBT_SOURCEMAC &&
+		    memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+			continue;
+		if (new_entry->bitmask & EBT_DESTMAC &&
+		    memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+			continue;
+		if (new_entry->bitmask != u_e->bitmask ||
+		    new_entry->invflags != u_e->invflags)
+			continue;
+		if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt !=
+		    u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt))
+			continue;
+		/* Compare all matches */
+		m_l = new_entry->m_list;
+		j = 0;
+		while (m_l) {
+			m = (struct ebt_u_match *)(m_l->m);
+			m_l2 = u_e->m_list;
+			while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
+				m_l2 = m_l2->next;
+			if (!m_l2 || !m->compare(m->m, m_l2->m))
+				goto letscontinue;
+			j++;
+			m_l = m_l->next;
+		}
+		/* Now be sure they have the same nr of matches */
+		k = 0;
+		m_l = u_e->m_list;
+		while (m_l) {
+			k++;
+			m_l = m_l->next;
+		}
+		if (j != k)
+			continue;
+
+		/* Compare all watchers */
+		w_l = new_entry->w_list;
+		j = 0;
+		while (w_l) {
+			w = (struct ebt_u_watcher *)(w_l->w);
+			w_l2 = u_e->w_list;
+			while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
+				w_l2 = w_l2->next;
+			if (!w_l2 || !w->compare(w->w, w_l2->w))
+				goto letscontinue;
+			j++;
+			w_l = w_l->next;
+		}
+		k = 0;
+		w_l = u_e->w_list;
+		while (w_l) {
+			k++;
+			w_l = w_l->next;
+		}
+		if (j != k)
+			continue;
+		if (strcmp(t->t->u.name, u_e->t->u.name))
+			continue;
+		if (!t->compare(t->t, u_e->t))
+			continue;
+		return i;
+letscontinue:;
+	}
+	return -1;
+}
+
+/* Add a rule, rule_nr is the rule to update
+ * rule_nr specifies where the rule should be inserted
+ * rule_nr > 0 : insert the rule right before the rule_nr'th rule
+ *               (the first rule is rule 1)
+ * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule,
+ *               where n denotes the number of rules in the chain
+ * rule_nr == 0: add a new rule at the end of the chain
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} and updates these
+ * pointers so that they point to ebt_{match,watcher,target}, before adding
+ * the rule to the chain. Don't free() the ebt_{match,watcher,target} and
+ * don't reuse the new_entry after a successful call to ebt_add_rule() */
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr)
+{
+	int i;
+	struct ebt_u_entry *u_e;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	struct ebt_cntchanges *cc, *new_cc;
+
+	if (rule_nr <= 0)
+		rule_nr += entries->nentries;
+	else
+		rule_nr--;
+	if (rule_nr > entries->nentries || rule_nr < 0) {
+		ebt_print_error("The specified rule number is incorrect");
+		return;
+	}
+	/* Go to the right position in the chain */
+	if (rule_nr == entries->nentries)
+		u_e = entries->entries;
+	else {
+		u_e = entries->entries->next;
+		for (i = 0; i < rule_nr; i++)
+			u_e = u_e->next;
+	}
+	/* We're adding one rule */
+	replace->nentries++;
+	entries->nentries++;
+	/* Insert the rule */
+	new_entry->next = u_e;
+	new_entry->prev = u_e->prev;
+	u_e->prev->next = new_entry;
+	u_e->prev = new_entry;
+	new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+	if (!new_cc)
+		ebt_print_memory();
+	new_cc->type = CNT_ADD;
+	new_cc->change = 0;
+	if (new_entry->next == entries->entries) {
+		for (i = replace->selected_chain+1; i < replace->num_chains; i++)
+			if (!replace->chains[i] || replace->chains[i]->nentries == 0)
+				continue;
+			else
+				break;
+		if (i == replace->num_chains)
+			cc = replace->cc;
+		else
+			cc = replace->chains[i]->entries->next->cc;
+	} else
+		cc = new_entry->next->cc;
+	new_cc->next = cc;
+	new_cc->prev = cc->prev;
+	cc->prev->next = new_cc;
+	cc->prev = new_cc;
+	new_entry->cc = new_cc;
+
+	/* Put the ebt_{match, watcher, target} pointers in place */
+	m_l = new_entry->m_list;
+	while (m_l) {
+		m_l->m = ((struct ebt_u_match *)m_l->m)->m;
+		m_l = m_l->next;
+	}
+	w_l = new_entry->w_list;
+	while (w_l) {
+		w_l->w = ((struct ebt_u_watcher *)w_l->w)->w;
+		w_l = w_l->next;
+	}
+	new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
+	/* Update the counter_offset of chains behind this one */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		entries = replace->chains[i];
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset++;
+	}
+}
+
+/* If *begin==*end==0 then find the rule corresponding to new_entry,
+ * else make the rule numbers positive (starting from 0) and check
+ * for bad rule numbers. */
+static int check_and_change_rule_number(struct ebt_u_replace *replace,
+   struct ebt_u_entry *new_entry, int *begin, int *end)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (*begin < 0)
+		*begin += entries->nentries + 1;
+	if (*end < 0)
+		*end += entries->nentries + 1;
+
+	if (*begin < 0 || *begin > *end || *end > entries->nentries) {
+		ebt_print_error("Sorry, wrong rule numbers");
+		return -1;
+	}
+
+	if ((*begin * *end == 0) && (*begin + *end != 0))
+		ebt_print_bug("begin and end should be either both zero, "
+			      "either both non-zero");
+	if (*begin != 0) {
+		(*begin)--;
+		(*end)--;
+	} else {
+		*begin = ebt_check_rule_exists(replace, new_entry);
+		*end = *begin;
+		if (*begin == -1) {
+			ebt_print_error("Sorry, rule does not exist");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Delete a rule or rules
+ * begin == end == 0: delete the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}. */
+void ebt_delete_rule(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end)
+{
+	int i,  nr_deletes;
+	struct ebt_u_entry *u_e, *u_e2, *u_e3;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+		return;
+	/* We're deleting rules */
+	nr_deletes = end - begin + 1;
+	replace->nentries -= nr_deletes;
+	entries->nentries -= nr_deletes;
+	/* Go to the right position in the chain */
+	u_e = entries->entries->next;
+	for (i = 0; i < begin; i++)
+		u_e = u_e->next;
+	u_e3 = u_e->prev;
+	/* Remove the rules */
+	for (i = 0; i < nr_deletes; i++) {
+		u_e2 = u_e;
+		ebt_delete_cc(u_e2->cc);
+		u_e = u_e->next;
+		/* Free everything */
+		ebt_free_u_entry(u_e2);
+		free(u_e2);
+	}
+	u_e3->next = u_e;
+	u_e->prev = u_e3;
+	/* Update the counter_offset of chains behind this one */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset -= nr_deletes;
+	}
+}
+
+/* Change the counters of a rule or rules
+ * begin == end == 0: change counters of the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}.
+ * The mask denotes the following:
+ *    pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement
+ *    bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment
+ * In daemon mode, mask==0 must hold */
+void ebt_change_counters(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end,
+		     struct ebt_counter *cnt, int mask)
+{
+	int i;
+	struct ebt_u_entry *u_e;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+		return;
+	u_e = entries->entries->next;
+	for (i = 0; i < begin; i++)
+		u_e = u_e->next;
+	for (i = end-begin+1; i > 0; i--) {
+		if (mask % 3 == 0) {
+			u_e->cnt.pcnt = (*cnt).pcnt;
+			u_e->cnt_surplus.pcnt = 0;
+		} else {
+#ifdef EBT_DEBUG
+			if (u_e->cc->type != CNT_NORM)
+				ebt_print_bug("cc->type != CNT_NORM");
+#endif
+			u_e->cnt_surplus.pcnt = (*cnt).pcnt;
+		}
+
+		if (mask / 3 == 0) {
+			u_e->cnt.bcnt = (*cnt).bcnt;
+			u_e->cnt_surplus.bcnt = 0;
+		} else {
+#ifdef EBT_DEBUG
+			if (u_e->cc->type != CNT_NORM)
+				ebt_print_bug("cc->type != CNT_NORM");
+#endif
+			u_e->cnt_surplus.bcnt = (*cnt).bcnt;
+		}
+		if (u_e->cc->type != CNT_ADD)
+			u_e->cc->type = CNT_CHANGE;
+		u_e->cc->change = mask;
+		u_e = u_e->next;
+	}
+}
+
+/* If selected_chain == -1 then zero all counters,
+ * otherwise, zero the counters of selected_chain */
+void ebt_zero_counters(struct ebt_u_replace *replace)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	struct ebt_u_entry *next;
+	int i;
+
+	if (!entries) {
+		for (i = 0; i < replace->num_chains; i++) {
+			if (!(entries = replace->chains[i]))
+				continue;
+			next = entries->entries->next;
+			while (next != entries->entries) {
+				if (next->cc->type == CNT_NORM)
+					next->cc->type = CNT_CHANGE;
+				next->cnt.bcnt = next->cnt.pcnt = 0;
+				next->cc->change = 0;
+				next = next->next;
+			}
+		}
+	} else {
+		if (entries->nentries == 0)
+			return;
+
+		next = entries->entries->next;
+		while (next != entries->entries) {
+			if (next->cc->type == CNT_NORM)
+				next->cc->type = CNT_CHANGE;
+			next->cnt.bcnt = next->cnt.pcnt = 0;
+			next = next->next;
+		}
+	}
+}
+
+/* Add a new chain and specify its policy */
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy)
+{
+	struct ebt_u_entries *new;
+
+	if (replace->num_chains == replace->max_chains)
+		ebt_double_chains(replace);
+	new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
+	if (!new)
+		ebt_print_memory();
+	replace->chains[replace->num_chains++] = new;
+	new->nentries = 0;
+	new->policy = policy;
+	new->counter_offset = replace->nentries;
+	new->hook_mask = 0;
+	strcpy(new->name, name);
+	new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+	if (!new->entries)
+		ebt_print_memory();
+	new->entries->next = new->entries->prev = new->entries;
+	new->kernel_start = NULL;
+}
+
+/* returns -1 if the chain is referenced, 0 on success */
+static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err)
+{
+	int tmp = replace->selected_chain;
+	/* If the chain is referenced, don't delete it,
+	 * also decrement jumps to a chain behind the
+	 * one we're deleting */
+	replace->selected_chain = chain;
+	if (ebt_check_for_references(replace, print_err))
+		return -1;
+	decrease_chain_jumps(replace);
+	ebt_flush_chains(replace);
+	replace->selected_chain = tmp;
+	free(replace->chains[chain]->entries);
+	free(replace->chains[chain]);
+	memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *));
+	replace->num_chains--;
+	return 0;
+}
+
+/* Selected_chain == -1: delete all non-referenced udc
+ * selected_chain < NF_BR_NUMHOOKS is illegal */
+void ebt_delete_chain(struct ebt_u_replace *replace)
+{
+	if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS)
+		ebt_print_bug("You can't remove a standard chain");
+	if (replace->selected_chain == -1) {
+		int i = NF_BR_NUMHOOKS;
+
+		while (i < replace->num_chains)
+			if (ebt_delete_a_chain(replace, i, 0))
+				i++;
+	} else
+		ebt_delete_a_chain(replace, replace->selected_chain, 1);
+}
+
+/* Rename an existing chain. */
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (!entries)
+		ebt_print_bug("ebt_rename_chain: entries == NULL");
+	strcpy(entries->name, name);
+}
+
+
+           /*
+*************************
+*************************
+**SPECIALIZED*FUNCTIONS**
+*************************
+*************************
+            */
+
+
+void ebt_double_chains(struct ebt_u_replace *replace)
+{
+	struct ebt_u_entries **new;
+
+	replace->max_chains *= 2;
+	new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *));
+	if (!new)
+		ebt_print_memory();
+	memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *));
+	free(replace->chains);
+	replace->chains = new;
+}
+
+/* Executes the final_check() function for all extensions used by the rule
+ * ebt_check_for_loops should have been executed earlier, to make sure the
+ * hook_mask is correct. The time argument to final_check() is set to 1,
+ * meaning it's the second time the final_check() function is executed. */
+void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
+			 struct ebt_u_entries *entries)
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_target *t;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+
+	m_l = e->m_list;
+	w_l = e->w_list;
+	while (m_l) {
+		m = ebt_find_match(m_l->m->u.name);
+		m->final_check(e, m_l->m, replace->name,
+		   entries->hook_mask, 1);
+		if (ebt_errormsg[0] != '\0')
+			return;
+		m_l = m_l->next;
+	}
+	while (w_l) {
+		w = ebt_find_watcher(w_l->w->u.name);
+		w->final_check(e, w_l->w, replace->name,
+		   entries->hook_mask, 1);
+		if (ebt_errormsg[0] != '\0')
+			return;
+		w_l = w_l->next;
+	}
+	t = ebt_find_target(e->t->u.name);
+	t->final_check(e, e->t, replace->name,
+	   entries->hook_mask, 1);
+}
+
+/* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err)
+{
+	if (print_err)
+		return iterate_entries(replace, 1);
+	else
+		return iterate_entries(replace, 2);
+}
+
+/* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS)
+ * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+                              int print_err)
+{
+	int tmp = replace->selected_chain, ret;
+
+	replace->selected_chain = chain_nr;
+	if (print_err)
+		ret = iterate_entries(replace, 1);
+	else
+		ret = iterate_entries(replace, 2);
+	replace->selected_chain = tmp;
+	return ret;
+}
+
+struct ebt_u_stack
+{
+	int chain_nr;
+	int n;
+	struct ebt_u_entry *e;
+	struct ebt_u_entries *entries;
+};
+
+/* Checks for loops
+ * As a by-product, the hook_mask member of each chain is filled in
+ * correctly. The check functions of the extensions need this hook_mask
+ * to know from which standard chains they can be called. */
+void ebt_check_for_loops(struct ebt_u_replace *replace)
+{
+	int chain_nr , i, j , k, sp = 0, verdict;
+	struct ebt_u_entries *entries, *entries2;
+	struct ebt_u_stack *stack = NULL;
+	struct ebt_u_entry *e;
+
+	/* Initialize hook_mask to 0 */
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		if (i < NF_BR_NUMHOOKS)
+			/* (1 << NF_BR_NUMHOOKS) implies it's a standard chain
+			 * (usefull in the final_check() funtions) */
+			entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
+		else
+			entries->hook_mask = 0;
+	}
+	if (replace->num_chains == NF_BR_NUMHOOKS)
+		return;
+	stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack));
+	if (!stack)
+		ebt_print_memory();
+
+	/* Check for loops, starting from every base chain */
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		chain_nr = i;
+
+		e = entries->entries->next;
+		for (j = 0; j < entries->nentries; j++) {
+			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET))
+				goto letscontinue;
+			verdict = ((struct ebt_standard_target *)(e->t))->verdict;
+			if (verdict < 0)
+				goto letscontinue;
+			/* Now see if we've been here before */
+			for (k = 0; k < sp; k++)
+				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) {
+					ebt_print_error("Loop from chain '%s' to chain '%s'",
+					   replace->chains[chain_nr]->name,
+					   replace->chains[stack[k].chain_nr]->name);
+					goto free_stack;
+				}
+			entries2 = replace->chains[verdict + NF_BR_NUMHOOKS];
+			/* check if we've dealt with this chain already */
+			if (entries2->hook_mask & (1<<i))
+				goto letscontinue;
+			entries2->hook_mask |= entries->hook_mask;
+			/* Jump to the chain, make sure we know how to get back */
+			stack[sp].chain_nr = chain_nr;
+			stack[sp].n = j;
+			stack[sp].entries = entries;
+			stack[sp].e = e;
+			sp++;
+			j = -1;
+			e = entries2->entries->next;
+			chain_nr = verdict + NF_BR_NUMHOOKS;
+			entries = entries2;
+			continue;
+letscontinue:
+			e = e->next;
+		}
+		/* We are at the end of a standard chain */
+		if (sp == 0)
+			continue;
+		/* Go back to the chain one level higher */
+		sp--;
+		j = stack[sp].n;
+		chain_nr = stack[sp].chain_nr;
+		e = stack[sp].e;
+		entries = stack[sp].entries;
+		goto letscontinue;
+	}
+free_stack:
+	free(stack);
+	return;
+}
+
+/* The user will use the match, so put it in new_entry. The ebt_u_match
+ * pointer is put in the ebt_entry_match pointer. ebt_add_rule will
+ * fill in the final value for new->m. Unless the rule is added to a chain,
+ * the pointer will keep pointing to the ebt_u_match (until the new_entry
+ * is freed). I know, I should use a union for these 2 pointer types... */
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m)
+{
+	struct ebt_u_match_list **m_list, *new;
+
+	for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+	new = (struct ebt_u_match_list *)
+	   malloc(sizeof(struct ebt_u_match_list));
+	if (!new)
+		ebt_print_memory();
+	*m_list = new;
+	new->next = NULL;
+	new->m = (struct ebt_entry_match *)m;
+}
+
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w)
+{
+	struct ebt_u_watcher_list **w_list;
+	struct ebt_u_watcher_list *new;
+
+	for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+	new = (struct ebt_u_watcher_list *)
+	   malloc(sizeof(struct ebt_u_watcher_list));
+	if (!new)
+		ebt_print_memory();
+	*w_list = new;
+	new->next = NULL;
+	new->w = (struct ebt_entry_watcher *)w;
+}
+
+
+        /*
+*******************
+*******************
+**OTHER*FUNCTIONS**
+*******************
+*******************
+         */
+
+
+/* type = 0 => update chain jumps
+ * type = 1 => check for reference, print error when referenced
+ * type = 2 => check for reference, don't print error when referenced
+ *
+ * Returns 1 when type == 1 and the chain is referenced
+ * returns 0 otherwise */
+static int iterate_entries(struct ebt_u_replace *replace, int type)
+{
+	int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS;
+	struct ebt_u_entries *entries;
+	struct ebt_u_entry *e;
+
+	if (chain_nr < 0)
+		ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr);
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		e = entries->entries->next;
+		for (j = 0; j < entries->nentries; j++) {
+			int chain_jmp;
+
+			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+				e = e->next;
+				continue;
+			}
+			chain_jmp = ((struct ebt_standard_target *)e->t)->
+				    verdict;
+			switch (type) {
+			case 1:
+			case 2:
+			if (chain_jmp == chain_nr) {
+				if (type == 2)
+					return 1;
+				ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d",
+				                replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, entries->name, j);
+				return 1;
+			}
+			break;
+			case 0:
+			/* Adjust the chain jumps when necessary */
+			if (chain_jmp > chain_nr)
+				((struct ebt_standard_target *)e->t)->verdict--;
+			break;
+			} /* End switch */
+			e = e->next;
+		}
+	}
+	return 0;
+}
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace)
+{
+	iterate_entries(replace, 0);
+}
+
+/* Used in initialization code of modules */
+void ebt_register_match(struct ebt_u_match *m)
+{
+	int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
+	struct ebt_u_match **i;
+
+	m->m = (struct ebt_entry_match *)malloc(size);
+	if (!m->m)
+		ebt_print_memory();
+	strcpy(m->m->u.name, m->name);
+	m->m->match_size = EBT_ALIGN(m->size);
+	m->init(m->m);
+
+	for (i = &ebt_matches; *i; i = &((*i)->next));
+	m->next = NULL;
+	*i = m;
+}
+
+void ebt_register_watcher(struct ebt_u_watcher *w)
+{
+	int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
+	struct ebt_u_watcher **i;
+
+	w->w = (struct ebt_entry_watcher *)malloc(size);
+	if (!w->w)
+		ebt_print_memory();
+	strcpy(w->w->u.name, w->name);
+	w->w->watcher_size = EBT_ALIGN(w->size);
+	w->init(w->w);
+
+	for (i = &ebt_watchers; *i; i = &((*i)->next));
+	w->next = NULL;
+	*i = w;
+}
+
+void ebt_register_target(struct ebt_u_target *t)
+{
+	int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
+	struct ebt_u_target **i;
+
+	t->t = (struct ebt_entry_target *)malloc(size);
+	if (!t->t)
+		ebt_print_memory();
+	strcpy(t->t->u.name, t->name);
+	t->t->target_size = EBT_ALIGN(t->size);
+	t->init(t->t);
+
+	for (i = &ebt_targets; *i; i = &((*i)->next));
+	t->next = NULL;
+	*i = t;
+}
+
+void ebt_register_table(struct ebt_u_table *t)
+{
+	t->next = ebt_tables;
+	ebt_tables = t;
+}
+
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *))
+{
+	struct ebt_u_match *i;
+
+	for (i = ebt_matches; i; i = i->next)
+		f(i);
+}
+
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *))
+{
+	struct ebt_u_watcher *i;
+
+	for (i = ebt_watchers; i; i = i->next)
+		f(i);
+}
+
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *))
+{
+	struct ebt_u_target *i;
+
+	for (i = ebt_targets; i; i = i->next)
+		f(i);
+}
+
+/* Don't use this function, use ebt_print_bug() */
+void __ebt_print_bug(char *file, int line, char *format, ...)
+{
+	va_list l;
+
+	va_start(l, format);
+	fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line);
+	vfprintf(stderr, format, l);
+	fprintf(stderr, "\n");
+	va_end(l);
+	exit (-1);
+}
+
+/* The error messages are put in here when ebt_silent == 1
+ * ebt_errormsg[0] == '\0' implies there was no error */
+char ebt_errormsg[ERRORMSG_MAXLEN];
+/* When error messages should not be printed on the screen, after which
+ * the program exit()s, set ebt_silent to 1. */
+int ebt_silent;
+/* Don't use this function, use ebt_print_error() */
+void __ebt_print_error(char *format, ...)
+{
+	va_list l;
+
+	va_start(l, format);
+	if (ebt_silent && ebt_errormsg[0] == '\0') {
+		vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
+		va_end(l);
+	} else {
+		vfprintf(stderr, format, l);
+		fprintf(stderr, ".\n");
+		va_end(l);
+		exit (-1);
+	}
+}
diff --git a/userspace/ebtables2/useful_functions.c b/userspace/ebtables2/useful_functions.c
new file mode 100644
index 0000000..d20b68e
--- /dev/null
+++ b/userspace/ebtables2/useful_functions.c
@@ -0,0 +1,413 @@
+/*
+ * useful_functions.c, January 2004
+ *
+ * Random collection of functions that can be used by extensions.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <stdio.h>
+#include <netinet/ether.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+const unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
+const unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
+const unsigned char msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255};
+
+/* 0: default, print only 2 digits if necessary
+ * 2: always print 2 digits, a printed mac address
+ * then always has the same length */
+int ebt_printstyle_mac;
+
+void ebt_print_mac(const unsigned char *mac)
+{
+	if (ebt_printstyle_mac == 2) {
+		int j;
+		for (j = 0; j < ETH_ALEN; j++)
+			printf("%02x%s", mac[j],
+				(j==ETH_ALEN-1) ? "" : ":");
+	} else
+		printf("%s", ether_ntoa((struct ether_addr *) mac));
+}
+
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
+{
+	char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+	if (!memcmp(mac, mac_type_unicast, 6) &&
+	    !memcmp(mask, msk_type_unicast, 6))
+		printf("Unicast");
+	else if (!memcmp(mac, mac_type_multicast, 6) &&
+	         !memcmp(mask, msk_type_multicast, 6))
+		printf("Multicast");
+	else if (!memcmp(mac, mac_type_broadcast, 6) &&
+	         !memcmp(mask, msk_type_broadcast, 6))
+		printf("Broadcast");
+	else if (!memcmp(mac, mac_type_bridge_group, 6) &&
+	         !memcmp(mask, msk_type_bridge_group, 6))
+		printf("BGA");
+	else {
+		ebt_print_mac(mac);
+		if (memcmp(mask, hlpmsk, 6)) {
+			printf("/");
+			ebt_print_mac(mask);
+		}
+	}
+}
+
+/* Checks the type for validity and calls getethertypebynumber(). */
+struct ethertypeent *parseethertypebynumber(int type)
+{
+	if (type < 1536)
+		ebt_print_error("Ethernet protocols have values >= 0x0600");
+	if (type > 0xffff)
+		ebt_print_error("Ethernet protocols have values <= 0xffff");
+	return getethertypebynumber(type);
+}
+
+/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
+int ebt_get_mac_and_mask(const char *from, unsigned char *to,
+  unsigned char *mask)
+{
+	char *p;
+	int i;
+	struct ether_addr *addr;
+
+	if (strcasecmp(from, "Unicast") == 0) {
+		memcpy(to, mac_type_unicast, ETH_ALEN);
+		memcpy(mask, msk_type_unicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Multicast") == 0) {
+		memcpy(to, mac_type_multicast, ETH_ALEN);
+		memcpy(mask, msk_type_multicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Broadcast") == 0) {
+		memcpy(to, mac_type_broadcast, ETH_ALEN);
+		memcpy(mask, msk_type_broadcast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "BGA") == 0) {
+		memcpy(to, mac_type_bridge_group, ETH_ALEN);
+		memcpy(mask, msk_type_bridge_group, ETH_ALEN);
+		return 0;
+	}
+	if ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		if (!(addr = ether_aton(p + 1)))
+			return -1;
+		memcpy(mask, addr, ETH_ALEN);
+	} else
+		memset(mask, 0xff, ETH_ALEN);
+	if (!(addr = ether_aton(from)))
+		return -1;
+	memcpy(to, addr, ETH_ALEN);
+	for (i = 0; i < ETH_ALEN; i++)
+		to[i] &= mask[i];
+	return 0;
+}
+
+/* 0: default
+ * 1: the inverse '!' of the option has already been specified */
+int ebt_invert = 0;
+
+/*
+ * Check if the inverse of the option is specified. This is used
+ * in the parse functions of the extensions and ebtables.c
+ */
+int _ebt_check_inverse(const char option[], int argc, char **argv)
+{
+	if (!option)
+		return ebt_invert;
+	if (strcmp(option, "!") == 0) {
+		if (ebt_invert == 1)
+			ebt_print_error("Double use of '!' not allowed");
+		if (optind >= argc)
+			optarg = NULL;
+		else
+			optarg = argv[optind];
+		optind++;
+		ebt_invert = 1;
+		return 1;
+	}
+	return ebt_invert;
+}
+
+/* Make sure the same option wasn't specified twice. This is used
+ * in the parse functions of the extensions and ebtables.c */
+void ebt_check_option(unsigned int *flags, unsigned int mask)
+{
+	if (*flags & mask)
+		ebt_print_error("Multiple use of same option not allowed");
+	*flags |= mask;
+}
+
+/* Put the ip string into 4 bytes. */
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+	char *p, *q, *end;
+	long int onebyte;
+	int i;
+	char buf[20];
+
+	strncpy(buf, ip, sizeof(buf) - 1);
+
+	p = buf;
+	for (i = 0; i < 3; i++) {
+		if ((q = strchr(p, '.')) == NULL)
+			return -1;
+		*q = '\0';
+		onebyte = strtol(p, &end, 10);
+		if (*end != '\0' || onebyte > 255 || onebyte < 0)   
+			return -1;
+		ip2[i] = (unsigned char)onebyte;
+		p = q + 1;
+	}
+
+	onebyte = strtol(p, &end, 10);
+	if (*end != '\0' || onebyte > 255 || onebyte < 0)
+		return -1;
+	ip2[3] = (unsigned char)onebyte;
+
+	return 0;
+}
+
+/* Put the mask into 4 bytes. */
+static int ip_mask(char *mask, unsigned char *mask2)
+{
+	char *end;
+	long int bits;
+	uint32_t mask22;
+
+	if (undot_ip(mask, mask2)) {
+		/* not the /a.b.c.e format, maybe the /x format */
+		bits = strtol(mask, &end, 10);
+		if (*end != '\0' || bits > 32 || bits < 0)
+			return -1;
+		if (bits != 0) {
+			mask22 = htonl(0xFFFFFFFF << (32 - bits));
+			memcpy(mask2, &mask22, 4);
+		} else {
+			mask22 = 0xFFFFFFFF;
+			memcpy(mask2, &mask22, 4);
+		}
+	}
+	return 0;
+}
+
+/* Set the ip mask and ip address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+{
+	char *p;
+
+	/* first the mask */
+	if ((p = strrchr(address, '/')) != NULL) {
+		*p = '\0';
+		if (ip_mask(p + 1, (unsigned char *)msk)) {
+			ebt_print_error("Problem with the IP mask '%s'", p + 1);
+			return;
+		}
+	} else
+		*msk = 0xFFFFFFFF;
+
+	if (undot_ip(address, (unsigned char *)addr)) {
+		ebt_print_error("Problem with the IP address '%s'", address);
+		return;
+	}
+	*addr = *addr & *msk;
+}
+
+
+/* Transform the ip mask into a string ready for output. */
+char *ebt_mask_to_dotted(uint32_t mask)
+{
+	int i;
+	static char buf[20];
+	uint32_t maskaddr, bits;
+
+	maskaddr = ntohl(mask);
+
+	/* don't print /32 */
+	if (mask == 0xFFFFFFFFL) {
+		*buf = '\0';
+		return buf;
+	}
+
+	i = 32;
+	bits = 0xFFFFFFFEL; /* Case 0xFFFFFFFF has just been dealt with */
+	while (--i >= 0 && maskaddr != bits)
+		bits <<= 1;
+
+	if (i > 0)
+		sprintf(buf, "/%d", i);
+	else if (!i)
+		*buf = '\0';
+	else
+		/* Mask was not a decent combination of 1's and 0's */
+		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], 
+		   ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
+		   ((unsigned char *)&mask)[3]);
+
+	return buf;
+}
+
+/* Most of the following code is derived from iptables */
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+	memcpy(dst, src, sizeof(struct in6_addr));
+}
+
+int string_to_number_ll(const char *s, unsigned long long min,
+            unsigned long long max, unsigned long long *ret)
+{
+	unsigned long long number;
+	char *end;
+
+	/* Handle hex, octal, etc. */
+	errno = 0;
+	number = strtoull(s, &end, 0);
+	if (*end == '\0' && end != s) {
+		/* we parsed a number, let's see if we want this */
+		if (errno != ERANGE && min <= number && (!max || number <= max)) {
+			*ret = number;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                       unsigned long *ret)
+{
+	int result;
+	unsigned long long number;
+
+	result = string_to_number_ll(s, min, max, &number);
+	*ret = (unsigned long)number;
+
+	return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+                     unsigned int *ret)
+{
+	int result;
+	unsigned long number;
+
+	result = string_to_number_l(s, min, max, &number);
+	*ret = (unsigned int)number;
+
+	return result;
+}
+
+static struct in6_addr *numeric_to_addr(const char *num)
+{
+	static struct in6_addr ap;
+	int err;
+
+	if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+		return &ap;
+	return (struct in6_addr *)NULL;
+}
+
+static struct in6_addr *parse_ip6_mask(char *mask)
+{
+	static struct in6_addr maskaddr;
+	struct in6_addr *addrp;
+	unsigned int bits;
+
+	if (mask == NULL) {
+		/* no mask at all defaults to 128 bits */
+		memset(&maskaddr, 0xff, sizeof maskaddr);
+		return &maskaddr;
+	}
+	if ((addrp = numeric_to_addr(mask)) != NULL)
+		return addrp;
+	if (string_to_number(mask, 0, 128, &bits) == -1)
+		ebt_print_error("Invalid IPv6 Mask '%s' specified", mask);
+	if (bits != 0) {
+		char *p = (char *)&maskaddr;
+		memset(p, 0xff, bits / 8);
+		memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+		p[bits / 8] = 0xff << (8 - (bits & 7));
+		return &maskaddr;
+	}
+
+	memset(&maskaddr, 0, sizeof maskaddr);
+	return &maskaddr;
+}
+
+/* Set the ipv6 mask and address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr,
+                           struct in6_addr *msk)
+{
+	struct in6_addr *tmp_addr;
+	char buf[256];
+	char *p;
+	int i;
+	int err;
+
+	strncpy(buf, address, sizeof(buf) - 1);
+	/* first the mask */
+	buf[sizeof(buf) - 1] = '\0';
+	if ((p = strrchr(buf, '/')) != NULL) {
+		*p = '\0';
+		tmp_addr = parse_ip6_mask(p + 1);
+	} else
+		tmp_addr = parse_ip6_mask(NULL);
+	in6addrcpy(msk, tmp_addr);
+
+	/* if a null mask is given, the name is ignored, like in "any/0" */
+	if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any)))
+		strcpy(buf, "::");
+
+	if ((err=inet_pton(AF_INET6, buf, addr)) < 1) {
+		ebt_print_error("Invalid IPv6 Address '%s' specified", buf);
+		return;
+	}
+
+	for (i = 0; i < 4; i++)
+		addr->s6_addr32[i] &= msk->s6_addr32[i];
+}
+
+/* Transform the ip6 addr into a string ready for output. */
+char *ebt_ip6_to_numeric(const struct in6_addr *addrp)
+{
+	/* 0000:0000:0000:0000:0000:000.000.000.000
+	 * 0000:0000:0000:0000:0000:0000:0000:0000 */
+	static char buf[50+1];
+	return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
diff --git a/userspace/libebtc/COPYING b/userspace/libebtc/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/userspace/libebtc/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/userspace/libebtc/ChangeLog b/userspace/libebtc/ChangeLog
new file mode 100644
index 0000000..72e878d
--- /dev/null
+++ b/userspace/libebtc/ChangeLog
@@ -0,0 +1,50 @@
+
+
+--------------------------------------------------------------------------------
+
+  Library for ethernet bridge tables
+  Version 0.1.0
+
+  Copyright 2005 by Jens Götze
+  All rights reserved.
+
+--------------------------------------------------------------------------------
+
+
+Changes from 0.0.4 to 0.1.0
+
+  2005-04-06: Jens Götze <jens@1in1.de>
+
+    - add function ebtc_target_jumptochain for standard targets (for
+      std->verdict)
+    - bugfix in ebtc_delete_chain: wrong adjust in chain list
+
+
+Changes from 0.0.3 to 0.0.4
+
+  2005-04-05: Jens Götze <jens@1in1.de>
+
+    - add same useful marcos to header file
+    - bugfix in ebtc.c: free got invalid pointer in end of ebtc_commit
+    - bugfix in ebtc.c: in ebtc_commit was handle not fully cleaned. this was
+      caused by a too small size with memset and had as consequence some
+      pointers was wrongly initialized.
+    - add entry check before append, insert or replace a entry.
+    - rename header file to libebtc.h
+
+
+Changes from 0.0.2 to 0.0.3
+
+  2005-04-05: Jens Götze <jens@1in1.de>
+
+    - modifications in function ebtc_commit: counter refresh for not changed
+      rules, after EBT_SO_SET_ENTRIES
+
+
+Changes from 0.0.1 to 0.0.2
+
+  2005-04-04: Jens Götze <jens@1in1.de>
+
+    - add a second test
+
+
diff --git a/userspace/libebtc/INSTALL b/userspace/libebtc/INSTALL
new file mode 100644
index 0000000..095b1eb
--- /dev/null
+++ b/userspace/libebtc/INSTALL
@@ -0,0 +1,231 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory.  After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/userspace/libebtc/Makefile.am b/userspace/libebtc/Makefile.am
new file mode 100644
index 0000000..291058b
--- /dev/null
+++ b/userspace/libebtc/Makefile.am
@@ -0,0 +1,47 @@
+#
+# ==[ Makefile ]===============================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# =============================================================================
+#
+
+
+SUBDIRS                     = . src include test
+
+MAINTAINERCLEANFILES        = Makefile.in
+
+EXTRA_DIST                  =
+
+AUTOMAKE_OPTIONS            = foreign
+
+
diff --git a/userspace/libebtc/Makefile.in b/userspace/libebtc/Makefile.in
new file mode 100644
index 0000000..dd4375a
--- /dev/null
+++ b/userspace/libebtc/Makefile.in
@@ -0,0 +1,624 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# ==[ Makefile ]===============================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# =============================================================================
+#
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = .
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in $(top_srcdir)/configure COPYING \
+	ChangeLog INSTALL config.guess config.sub depcomp install-sh \
+	ltmain.sh missing
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno configure.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+	html-recursive info-recursive install-data-recursive \
+	install-exec-recursive install-info-recursive \
+	install-recursive installcheck-recursive installdirs-recursive \
+	pdf-recursive ps-recursive uninstall-info-recursive \
+	uninstall-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  { test ! -d $(distdir) \
+    || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+         && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+EBTC_LT_AGE = @EBTC_LT_AGE@
+EBTC_LT_CURRENT = @EBTC_LT_CURRENT@
+EBTC_LT_REVISION = @EBTC_LT_REVISION@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+SUBDIRS = . src include test
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = 
+AUTOMAKE_OPTIONS = foreign
+all: all-recursive
+
+.SUFFIXES:
+am--refresh:
+	@:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+	      cd $(srcdir) && $(AUTOMAKE) --foreign  \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --foreign  Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    echo ' $(SHELL) ./config.status'; \
+	    $(SHELL) ./config.status;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+	@set fnord $$MAKEFLAGS; amf=$$2; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+	@set fnord $$MAKEFLAGS; amf=$$2; \
+	dot_seen=no; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	rev=''; for subdir in $$list; do \
+	  if test "$$subdir" = "."; then :; else \
+	    rev="$$subdir $$rev"; \
+	  fi; \
+	done; \
+	rev="$$rev ."; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+ctags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	$(am__remove_distdir)
+	mkdir $(distdir)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    dir="/$$dir"; \
+	    $(mkdir_p) "$(distdir)$$dir"; \
+	  else \
+	    dir=''; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+	list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d "$(distdir)/$$subdir" \
+	    || $(mkdir_p) "$(distdir)/$$subdir" \
+	    || exit 1; \
+	    distdir=`$(am__cd) $(distdir) && pwd`; \
+	    top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+	    (cd $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$top_distdir" \
+	        distdir="$$distdir/$$subdir" \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+	-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
+	|| chmod -R a+r $(distdir)
+dist-gzip: distdir
+	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+	$(am__remove_distdir)
+
+dist-bzip2: distdir
+	tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+	$(am__remove_distdir)
+
+dist-tarZ: distdir
+	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+	$(am__remove_distdir)
+
+dist-shar: distdir
+	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+	$(am__remove_distdir)
+
+dist-zip: distdir
+	-rm -f $(distdir).zip
+	zip -rq $(distdir).zip $(distdir)
+	$(am__remove_distdir)
+
+dist dist-all: distdir
+	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+	$(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	case '$(DIST_ARCHIVES)' in \
+	*.tar.gz*) \
+	  GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+	*.tar.bz2*) \
+	  bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+	*.tar.Z*) \
+	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+	*.shar.gz*) \
+	  GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+	*.zip*) \
+	  unzip $(distdir).zip ;;\
+	esac
+	chmod -R a-w $(distdir); chmod a+w $(distdir)
+	mkdir $(distdir)/_build
+	mkdir $(distdir)/_inst
+	chmod a-w $(distdir)
+	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+	  && cd $(distdir)/_build \
+	  && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+	        distuninstallcheck \
+	  && chmod -R a-w "$$dc_install_base" \
+	  && ({ \
+	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
+	  && rm -rf "$$dc_destdir" \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist \
+	  && rm -rf $(DIST_ARCHIVES) \
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+	$(am__remove_distdir)
+	@(echo "$(distdir) archives ready for distribution: "; \
+	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+	  sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
+distuninstallcheck:
+	@cd $(distuninstallcheck_dir) \
+	&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+	   || { echo "ERROR: files left after uninstall:" ; \
+	        if test -n "$(DESTDIR)"; then \
+	          echo "  (check DESTDIR support)"; \
+	        fi ; \
+	        $(distuninstallcheck_listfiles) ; \
+	        exit 1; } >&2
+distcleancheck: distclean
+	@if test '$(srcdir)' = . ; then \
+	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+	  exit 1 ; \
+	fi
+	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+	  || { echo "ERROR: files left in build directory after distclean:" ; \
+	       $(distcleancheck_listfiles) ; \
+	       exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-libtool \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-recursive
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf $(top_srcdir)/autom4te.cache
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+uninstall-info: uninstall-info-recursive
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
+	check-am clean clean-generic clean-libtool clean-recursive \
+	ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \
+	dist-shar dist-tarZ dist-zip distcheck distclean \
+	distclean-generic distclean-libtool distclean-recursive \
+	distclean-tags distcleancheck distdir distuninstallcheck dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-exec install-exec-am \
+	install-info install-info-am install-man install-strip \
+	installcheck installcheck-am installdirs installdirs-am \
+	maintainer-clean maintainer-clean-generic \
+	maintainer-clean-recursive mostlyclean mostlyclean-generic \
+	mostlyclean-libtool mostlyclean-recursive pdf pdf-am ps ps-am \
+	tags tags-recursive uninstall uninstall-am uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/userspace/libebtc/README b/userspace/libebtc/README
new file mode 100644
index 0000000..cffa2d5
--- /dev/null
+++ b/userspace/libebtc/README
@@ -0,0 +1,16 @@
+
+
+--------------------------------------------------------------------------------
+
+  Library for ethernet bridge tables
+  Version 0.1.0
+
+  Copyright 2005 by Jens Götze
+  All rights reserved.
+
+--------------------------------------------------------------------------------
+
+
+For compile start ./autogen.sh
+
+
diff --git a/userspace/libebtc/ToDo b/userspace/libebtc/ToDo
new file mode 100644
index 0000000..599ed43
--- /dev/null
+++ b/userspace/libebtc/ToDo
@@ -0,0 +1,21 @@
+
+
+--------------------------------------------------------------------------------
+
+  Library for ethernet bridge tables
+  Version 0.1.0
+
+  Copyright 2005 by Jens Götze
+  All rights reserved.
+
+--------------------------------------------------------------------------------
+
+
+Conditions for release 1.0:
+
+  2005-04-04: Jens Götze <jens@1in1.de>
+
+    [ ] Better documentation of functions, types and macros.
+    [ ] 64 Bit Kernel and 32 Bit Userspace casts.
+
+
diff --git a/userspace/libebtc/aclocal.m4 b/userspace/libebtc/aclocal.m4
new file mode 100644
index 0000000..181fe2c
--- /dev/null
+++ b/userspace/libebtc/aclocal.m4
@@ -0,0 +1,6976 @@
+# generated automatically by aclocal 1.9.3 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+
+# serial 47 AC_PROG_LIBTOOL
+# Debian $Rev: 214 $
+
+
+# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
+# -----------------------------------------------------------
+# If this macro is not defined by Autoconf, define it here.
+m4_ifdef([AC_PROVIDE_IFELSE],
+         [],
+         [m4_define([AC_PROVIDE_IFELSE],
+	         [m4_ifdef([AC_PROVIDE_$1],
+		           [$2], [$3])])])
+
+
+# AC_PROG_LIBTOOL
+# ---------------
+AC_DEFUN([AC_PROG_LIBTOOL],
+[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl
+dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX
+dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX.
+  AC_PROVIDE_IFELSE([AC_PROG_CXX],
+    [AC_LIBTOOL_CXX],
+    [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX
+  ])])
+dnl And a similar setup for Fortran 77 support
+  AC_PROVIDE_IFELSE([AC_PROG_F77],
+    [AC_LIBTOOL_F77],
+    [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77
+])])
+
+dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly.
+dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run
+dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both.
+  AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+    [AC_LIBTOOL_GCJ],
+    [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+      [AC_LIBTOOL_GCJ],
+      [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],
+	[AC_LIBTOOL_GCJ],
+      [ifdef([AC_PROG_GCJ],
+	     [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])
+       ifdef([A][M_PROG_GCJ],
+	     [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])])
+       ifdef([LT_AC_PROG_GCJ],
+	     [define([LT_AC_PROG_GCJ],
+		defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])])
+])])# AC_PROG_LIBTOOL
+
+
+# _AC_PROG_LIBTOOL
+# ----------------
+AC_DEFUN([_AC_PROG_LIBTOOL],
+[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl
+AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl
+AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl
+AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+# Prevent multiple expansion
+define([AC_PROG_LIBTOOL], [])
+])# _AC_PROG_LIBTOOL
+
+
+# AC_LIBTOOL_SETUP
+# ----------------
+AC_DEFUN([AC_LIBTOOL_SETUP],
+[AC_PREREQ(2.50)dnl
+AC_REQUIRE([AC_ENABLE_SHARED])dnl
+AC_REQUIRE([AC_ENABLE_STATIC])dnl
+AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_LD])dnl
+AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl
+AC_REQUIRE([AC_PROG_NM])dnl
+
+AC_REQUIRE([AC_PROG_LN_S])dnl
+AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl
+# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers!
+AC_REQUIRE([AC_OBJEXT])dnl
+AC_REQUIRE([AC_EXEEXT])dnl
+dnl
+
+AC_LIBTOOL_SYS_MAX_CMD_LEN
+AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
+AC_LIBTOOL_OBJDIR
+
+AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
+_LT_AC_PROG_ECHO_BACKSLASH
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g']
+
+# Same as above, but do not quote variable references.
+[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g']
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Constants:
+rm="rm -f"
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except M$VC,
+# which needs '.lib').
+libext=a
+ltmain="$ac_aux_dir/ltmain.sh"
+ofile="$default_ofile"
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+AC_CHECK_TOOL(AR, ar, false)
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+AC_CHECK_TOOL(STRIP, strip, :)
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+test -z "$AS" && AS=as
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$LD" && LD=ld
+test -z "$LN_S" && LN_S="ln -s"
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+test -z "$NM" && NM=nm
+test -z "$SED" && SED=sed
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$RANLIB" && RANLIB=:
+test -z "$STRIP" && STRIP=:
+test -z "$ac_objext" && ac_objext=o
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds"
+    ;;
+  *)
+    old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+# Only perform the check for file, if the check method requires it
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    AC_PATH_MAGIC
+  fi
+  ;;
+esac
+
+AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no)
+AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL],
+enable_win32_dll=yes, enable_win32_dll=no)
+
+AC_ARG_ENABLE([libtool-lock],
+    [AC_HELP_STRING([--disable-libtool-lock],
+	[avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+AC_ARG_WITH([pic],
+    [AC_HELP_STRING([--with-pic],
+	[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [pic_mode="$withval"],
+    [pic_mode=default])
+test -z "$pic_mode" && pic_mode=default
+
+# Use C for the default configuration in the libtool script
+tagname=
+AC_LIBTOOL_LANG_C_CONFIG
+_LT_AC_TAGCONFIG
+])# AC_LIBTOOL_SETUP
+
+
+# _LT_AC_SYS_COMPILER
+# -------------------
+AC_DEFUN([_LT_AC_SYS_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_AC_SYS_COMPILER
+
+
+# _LT_AC_SYS_LIBPATH_AIX
+# ----------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX],
+[AC_LINK_IFELSE(AC_LANG_PROGRAM,[
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi],[])
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+])# _LT_AC_SYS_LIBPATH_AIX
+
+
+# _LT_AC_SHELL_INIT(ARG)
+# ----------------------
+AC_DEFUN([_LT_AC_SHELL_INIT],
+[ifdef([AC_DIVERSION_NOTICE],
+	     [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+	 [AC_DIVERT_PUSH(NOTICE)])
+$1
+AC_DIVERT_POP
+])# _LT_AC_SHELL_INIT
+
+
+# _LT_AC_PROG_ECHO_BACKSLASH
+# --------------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH],
+[_LT_AC_SHELL_INIT([
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+  ;;
+esac
+
+echo=${ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X[$]1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+[$]*
+EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+if test -z "$ECHO"; then
+if test "X${echo_test_string+set}" != Xset; then
+# find a string as large as possible, as long as the shell can cope with it
+  for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+       echo_test_string="`eval $cmd`" &&
+       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null
+    then
+      break
+    fi
+  done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+   echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+   test "X$echo_testing_string" = "X$echo_test_string"; then
+  :
+else
+  # The Solaris, AIX, and Digital Unix default echo programs unquote
+  # backslashes.  This makes it impossible to quote backslashes using
+  #   echo "$something" | sed 's/\\/\\\\/g'
+  #
+  # So, first we look for a working echo in the user's PATH.
+
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for dir in $PATH /usr/ucb; do
+    IFS="$lt_save_ifs"
+    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      echo="$dir/echo"
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+
+  if test "X$echo" = Xecho; then
+    # We didn't find a better echo, so look for alternatives.
+    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      # This shell has a builtin print -r that does the trick.
+      echo='print -r'
+    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+	 test "X$CONFIG_SHELL" != X/bin/ksh; then
+      # If we have ksh, try running configure again with it.
+      ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+      export ORIGINAL_CONFIG_SHELL
+      CONFIG_SHELL=/bin/ksh
+      export CONFIG_SHELL
+      exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+    else
+      # Try using printf.
+      echo='printf %s\n'
+      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+	 echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+	 test "X$echo_testing_string" = "X$echo_test_string"; then
+	# Cool, printf works
+	:
+      elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+	export CONFIG_SHELL
+	SHELL="$CONFIG_SHELL"
+	export SHELL
+	echo="$CONFIG_SHELL [$]0 --fallback-echo"
+      elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	echo="$CONFIG_SHELL [$]0 --fallback-echo"
+      else
+	# maybe with a smaller string...
+	prev=:
+
+	for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+	  if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null
+	  then
+	    break
+	  fi
+	  prev="$cmd"
+	done
+
+	if test "$prev" != 'sed 50q "[$]0"'; then
+	  echo_test_string=`eval $prev`
+	  export echo_test_string
+	  exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+	else
+	  # Oops.  We lost completely, so just stick with echo.
+	  echo=echo
+	fi
+      fi
+    fi
+  fi
+fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+ECHO=$echo
+if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+   ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(ECHO)
+])])# _LT_AC_PROG_ECHO_BACKSLASH
+
+
+# _LT_AC_LOCK
+# -----------
+AC_DEFUN([_LT_AC_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+    [AC_HELP_STRING([--disable-libtool-lock],
+	[avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *ELF-32*)
+      HPUX_IA64_MODE="32"
+      ;;
+    *ELF-64*)
+      HPUX_IA64_MODE="64"
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+   if test "$lt_cv_prog_gnu_ld" = yes; then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -melf32bsmip"
+      ;;
+    *N32*)
+      LD="${LD-ld} -melf32bmipn32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -melf64bmip"
+      ;;
+    esac
+   else
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -32"
+      ;;
+    *N32*)
+      LD="${LD-ld} -n32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -64"
+      ;;
+    esac
+   fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case "`/usr/bin/file conftest.o`" in
+    *32-bit*)
+      case $host in
+        x86_64-*linux*)
+          LD="${LD-ld} -m elf_i386"
+          ;;
+        ppc64-*linux*|powerpc64-*linux*)
+          LD="${LD-ld} -m elf32ppclinux"
+          ;;
+        s390x-*linux*)
+          LD="${LD-ld} -m elf_s390"
+          ;;
+        sparc64-*linux*)
+          LD="${LD-ld} -m elf32_sparc"
+          ;;
+      esac
+      ;;
+    *64-bit*)
+      case $host in
+        x86_64-*linux*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        ppc*-*linux*|powerpc*-*linux*)
+          LD="${LD-ld} -m elf64ppc"
+          ;;
+        s390*-*linux*)
+          LD="${LD-ld} -m elf64_s390"
+          ;;
+        sparc*-*linux*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL],
+[*-*-cygwin* | *-*-mingw* | *-*-pw32*)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+  ])
+esac
+
+need_locks="$enable_libtool_lock"
+
+])# _LT_AC_LOCK
+
+
+# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#		[OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION],
+[AC_REQUIRE([LT_AC_PROG_SED])
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+  ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       $2=yes
+     fi
+   fi
+   $rm conftest*
+])
+
+if test x"[$]$2" = xyes; then
+    ifelse([$5], , :, [$5])
+else
+    ifelse([$6], , :, [$6])
+fi
+])# AC_LIBTOOL_COMPILER_OPTION
+
+
+# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                          [ACTION-SUCCESS], [ACTION-FAILURE])
+# ------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([AC_LIBTOOL_LINKER_OPTION],
+[AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $3"
+   printf "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+     else
+       $2=yes
+     fi
+   fi
+   $rm conftest*
+   LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+    ifelse([$4], , :, [$4])
+else
+    ifelse([$5], , :, [$5])
+fi
+])# AC_LIBTOOL_LINKER_OPTION
+
+
+# AC_LIBTOOL_SYS_MAX_CMD_LEN
+# --------------------------
+AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN],
+[# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+ *)
+    # If test is not a shell built-in, we'll probably end up computing a
+    # maximum length that is only half of the actual maximum length, but
+    # we can't tell.
+    while (test "X"`$CONFIG_SHELL [$]0 --fallback-echo "X$teststring" 2>/dev/null` \
+	       = "XX$teststring") >/dev/null 2>&1 &&
+	    new_result=`expr "X$teststring" : ".*" 2>&1` &&
+	    lt_cv_sys_max_cmd_len=$new_result &&
+	    test $i != 17 # 1/2 MB should be enough
+    do
+      i=`expr $i + 1`
+      teststring=$teststring$teststring
+    done
+    teststring=
+    # Add a significant safety factor because C++ compilers can tack on massive
+    # amounts of additional arguments before passing them to the linker.
+    # It appears as though 1/2 is a usable value.
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    ;;
+  esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+])# AC_LIBTOOL_SYS_MAX_CMD_LEN
+
+
+# _LT_AC_CHECK_DLFCN
+# --------------------
+AC_DEFUN([_LT_AC_CHECK_DLFCN],
+[AC_CHECK_HEADERS(dlfcn.h)dnl
+])# _LT_AC_CHECK_DLFCN
+
+
+# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                           ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ------------------------------------------------------------------
+AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF],
+[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}]
+EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_unknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_AC_TRY_DLOPEN_SELF
+
+
+# AC_LIBTOOL_DLOPEN_SELF
+# -------------------
+AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF],
+[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+   ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+   ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+	  [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+	    [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"],
+	[AC_CHECK_FUNC([dlopen],
+	      [lt_cv_dlopen="dlopen"],
+	  [AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+	    [AC_CHECK_LIB([svld], [dlopen],
+		  [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+	      [AC_CHECK_LIB([dld], [dld_link],
+		    [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"])
+	      ])
+	    ])
+	  ])
+	])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+	  lt_cv_dlopen_self, [dnl
+	  _LT_AC_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+	    lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+    	  lt_cv_dlopen_self_static, [dnl
+	  _LT_AC_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+	    lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+])# AC_LIBTOOL_DLOPEN_SELF
+
+
+# AC_LIBTOOL_PROG_CC_C_O([TAGNAME])
+# ---------------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler
+AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O],
+[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $rm -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s out/conftest.err; then
+       _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w .
+   $rm conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
+   $rm out/* && rmdir out
+   cd ..
+   rmdir conftest
+   $rm conftest*
+])
+])# AC_LIBTOOL_PROG_CC_C_O
+
+
+# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME])
+# -----------------------------------------
+# Check to see if we can do hard links to lock some files if needed
+AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS],
+[AC_REQUIRE([_LT_AC_LOCK])dnl
+
+hard_links="nottested"
+if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS
+
+
+# AC_LIBTOOL_OBJDIR
+# -----------------
+AC_DEFUN([AC_LIBTOOL_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+])# AC_LIBTOOL_OBJDIR
+
+
+# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME])
+# ----------------------------------------------
+# Check hardcoding attributes.
+AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_AC_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \
+   test -n "$_LT_AC_TAGVAR(runpath_var $1)" || \
+   test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)"="Xyes" ; then
+
+  # We can hardcode non-existant directories.
+  if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+     test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_AC_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_AC_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_AC_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH
+
+
+# AC_LIBTOOL_SYS_LIB_STRIP
+# ------------------------
+AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP],
+[striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+   darwin*)
+       if test -n "$STRIP" ; then
+         striplib="$STRIP -x"
+         AC_MSG_RESULT([yes])
+       else
+  AC_MSG_RESULT([no])
+fi
+       ;;
+   *)
+  AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+])# AC_LIBTOOL_SYS_LIB_STRIP
+
+
+# AC_LIBTOOL_SYS_DYNAMIC_LINKER
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER],
+[AC_MSG_CHECKING([dynamic linker characteristics])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+if test "$GCC" = yes; then
+  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
+  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
+  if test "$GCC" = yes; then
+    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
+  else
+    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
+  fi
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+kfreebsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  *) # from 3.2 on
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case "$host_cpu" in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+   hppa*64*)
+     shrext_cmds='.sl'
+     hardcode_into_libs=yes
+     dynamic_linker="$host_os dld.sl"
+     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+     soname_spec='${libname}${release}${shared_ext}$major'
+     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+     ;;
+   *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`$SED -e 's/[:,\t]/ /g;s/=[^=]*$//;s/=[^= ]* / /g' /etc/ld.so.conf | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+knetbsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+nto-qnx*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=yes
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[[89]] | openbsd2.[[89]].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+])# AC_LIBTOOL_SYS_DYNAMIC_LINKER
+
+
+# _LT_AC_TAGCONFIG
+# ----------------
+AC_DEFUN([_LT_AC_TAGCONFIG],
+[AC_ARG_WITH([tags],
+    [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@],
+        [include additional configurations @<:@automatic@:>@])],
+    [tagnames="$withval"])
+
+if test -f "$ltmain" && test -n "$tagnames"; then
+  if test ! -f "${ofile}"; then
+    AC_MSG_WARN([output file `$ofile' does not exist])
+  fi
+
+  if test -z "$LTCC"; then
+    eval "`$SHELL ${ofile} --config | grep '^LTCC='`"
+    if test -z "$LTCC"; then
+      AC_MSG_WARN([output file `$ofile' does not look like a libtool script])
+    else
+      AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile'])
+    fi
+  fi
+
+  # Extract list of available tagged configurations in $ofile.
+  # Note that this assumes the entire list is on one line.
+  available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'`
+
+  lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+  for tagname in $tagnames; do
+    IFS="$lt_save_ifs"
+    # Check whether tagname contains only valid characters
+    case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in
+    "") ;;
+    *)  AC_MSG_ERROR([invalid tag name: $tagname])
+	;;
+    esac
+
+    if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null
+    then
+      AC_MSG_ERROR([tag name \"$tagname\" already exists])
+    fi
+
+    # Update the list of available tags.
+    if test -n "$tagname"; then
+      echo appending configuration tag \"$tagname\" to $ofile
+
+      case $tagname in
+      CXX)
+	if test -n "$CXX" && test "X$CXX" != "Xno"; then
+	  AC_LIBTOOL_LANG_CXX_CONFIG
+	else
+	  tagname=""
+	fi
+	;;
+
+      F77)
+	if test -n "$F77" && test "X$F77" != "Xno"; then
+	  AC_LIBTOOL_LANG_F77_CONFIG
+	else
+	  tagname=""
+	fi
+	;;
+
+      GCJ)
+	if test -n "$GCJ" && test "X$GCJ" != "Xno"; then
+	  AC_LIBTOOL_LANG_GCJ_CONFIG
+	else
+	  tagname=""
+	fi
+	;;
+
+      RC)
+	AC_LIBTOOL_LANG_RC_CONFIG
+	;;
+
+      *)
+	AC_MSG_ERROR([Unsupported tag name: $tagname])
+	;;
+      esac
+
+      # Append the new tag name to the list of available tags.
+      if test -n "$tagname" ; then
+      available_tags="$available_tags $tagname"
+    fi
+    fi
+  done
+  IFS="$lt_save_ifs"
+
+  # Now substitute the updated list of available tags.
+  if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then
+    mv "${ofile}T" "$ofile"
+    chmod +x "$ofile"
+  else
+    rm -f "${ofile}T"
+    AC_MSG_ERROR([unable to update list of available tagged configurations.])
+  fi
+fi
+])# _LT_AC_TAGCONFIG
+
+
+# AC_LIBTOOL_DLOPEN
+# -----------------
+# enable checks for dlopen support
+AC_DEFUN([AC_LIBTOOL_DLOPEN],
+ [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])
+])# AC_LIBTOOL_DLOPEN
+
+
+# AC_LIBTOOL_WIN32_DLL
+# --------------------
+# declare package support for building win32 dll's
+AC_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_BEFORE([$0], [AC_LIBTOOL_SETUP])
+])# AC_LIBTOOL_WIN32_DLL
+
+
+# AC_ENABLE_SHARED([DEFAULT])
+# ---------------------------
+# implement the --enable-shared flag
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+AC_DEFUN([AC_ENABLE_SHARED],
+[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE([shared],
+    [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+	[build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_shared=]AC_ENABLE_SHARED_DEFAULT)
+])# AC_ENABLE_SHARED
+
+
+# AC_DISABLE_SHARED
+# -----------------
+#- set the default shared flag to --disable-shared
+AC_DEFUN([AC_DISABLE_SHARED],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_SHARED(no)
+])# AC_DISABLE_SHARED
+
+
+# AC_ENABLE_STATIC([DEFAULT])
+# ---------------------------
+# implement the --enable-static flag
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+AC_DEFUN([AC_ENABLE_STATIC],
+[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE([static],
+    [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+	[build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_static=]AC_ENABLE_STATIC_DEFAULT)
+])# AC_ENABLE_STATIC
+
+
+# AC_DISABLE_STATIC
+# -----------------
+# set the default static flag to --disable-static
+AC_DEFUN([AC_DISABLE_STATIC],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_STATIC(no)
+])# AC_DISABLE_STATIC
+
+
+# AC_ENABLE_FAST_INSTALL([DEFAULT])
+# ---------------------------------
+# implement the --enable-fast-install flag
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+AC_DEFUN([AC_ENABLE_FAST_INSTALL],
+[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl
+AC_ARG_ENABLE([fast-install],
+    [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT)
+])# AC_ENABLE_FAST_INSTALL
+
+
+# AC_DISABLE_FAST_INSTALL
+# -----------------------
+# set the default to --disable-fast-install
+AC_DEFUN([AC_DISABLE_FAST_INSTALL],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+AC_ENABLE_FAST_INSTALL(no)
+])# AC_DISABLE_FAST_INSTALL
+
+
+# AC_LIBTOOL_PICMODE([MODE])
+# --------------------------
+# implement the --with-pic flag
+# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
+AC_DEFUN([AC_LIBTOOL_PICMODE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+pic_mode=ifelse($#,1,$1,default)
+])# AC_LIBTOOL_PICMODE
+
+
+# AC_PROG_EGREP
+# -------------
+# This is predefined starting with Autoconf 2.54, so this conditional
+# definition can be removed once we require Autoconf 2.54 or later.
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP],
+[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep],
+   [if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi])
+ EGREP=$ac_cv_prog_egrep
+ AC_SUBST([EGREP])
+])])
+
+
+# AC_PATH_TOOL_PREFIX
+# -------------------
+# find a file program which can recognise shared library
+AC_DEFUN([AC_PATH_TOOL_PREFIX],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="ifelse([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+])# AC_PATH_TOOL_PREFIX
+
+
+# AC_PATH_MAGIC
+# -------------
+# find a file program which can recognise a shared library
+AC_DEFUN([AC_PATH_MAGIC],
+[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# AC_PATH_MAGIC
+
+
+# AC_PROG_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([AC_PROG_LD],
+[AC_ARG_WITH([gnu-ld],
+    [AC_HELP_STRING([--with-gnu-ld],
+	[assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test "$withval" = no || with_gnu_ld=yes],
+    [with_gnu_ld=no])
+AC_REQUIRE([LT_AC_PROG_SED])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_PROG_LD_GNU
+])# AC_PROG_LD
+
+
+# AC_PROG_LD_GNU
+# --------------
+AC_DEFUN([AC_PROG_LD_GNU],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# AC_PROG_LD_GNU
+
+
+# AC_PROG_LD_RELOAD_FLAG
+# ----------------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+AC_DEFUN([AC_PROG_LD_RELOAD_FLAG],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+])# AC_PROG_LD_RELOAD_FLAG
+
+
+# AC_DEPLIBS_CHECK_METHOD
+# -----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+AC_DEFUN([AC_DEPLIBS_CHECK_METHOD],
+[AC_CACHE_CHECK([how to recognise dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix4* | aix5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi4*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump'.
+  lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | kfreebsd*-gnu)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case "$host_cpu" in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+nto-qnx*)
+  lt_cv_deplibs_check_method=unknown
+  ;;
+
+openbsd*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB shared object'
+  else
+    lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sco3.2v5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+])# AC_DEPLIBS_CHECK_METHOD
+
+
+# AC_PROG_NM
+# ----------
+# find the pathname to a BSD-compatible name lister
+AC_DEFUN([AC_PROG_NM],
+[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    tmp_nm="$ac_dir/${ac_tool_prefix}nm"
+    if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+      # Check to see if the nm accepts a BSD-compat flag.
+      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+      #   nm: unknown option "B" ignored
+      # Tru64's nm complains that /dev/null is an invalid object file
+      case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+      */dev/null* | *'Invalid file or object type'*)
+	lt_cv_path_NM="$tmp_nm -B"
+	break
+        ;;
+      *)
+	case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	*/dev/null*)
+	  lt_cv_path_NM="$tmp_nm -p"
+	  break
+	  ;;
+	*)
+	  lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	  continue # so that we can try to find one that supports BSD flags
+	  ;;
+	esac
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+  test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm
+fi])
+NM="$lt_cv_path_NM"
+])# AC_PROG_NM
+
+
+# AC_CHECK_LIBM
+# -------------
+# check for math library
+AC_DEFUN([AC_CHECK_LIBM],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM="-lm")
+  ;;
+esac
+])# AC_CHECK_LIBM
+
+
+# AC_LIBLTDL_CONVENIENCE([DIRECTORY])
+# -----------------------------------
+# sets LIBLTDL to the link flags for the libltdl convenience library and
+# LTDLINCL to the include flags for the libltdl header and adds
+# --enable-ltdl-convenience to the configure arguments.  Note that LIBLTDL
+# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called.  If
+# DIRECTORY is not provided, it is assumed to be `libltdl'.  LIBLTDL will
+# be prefixed with '${top_builddir}/' and LTDLINCL will be prefixed with
+# '${top_srcdir}/' (note the single quotes!).  If your package is not
+# flat and you're not using automake, define top_builddir and
+# top_srcdir appropriately in the Makefiles.
+AC_DEFUN([AC_LIBLTDL_CONVENIENCE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  case $enable_ltdl_convenience in
+  no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;;
+  "") enable_ltdl_convenience=yes
+      ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;;
+  esac
+  LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la
+  LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
+  # For backwards non-gettext consistent compatibility...
+  INCLTDL="$LTDLINCL"
+])# AC_LIBLTDL_CONVENIENCE
+
+
+# AC_LIBLTDL_INSTALLABLE([DIRECTORY])
+# -----------------------------------
+# sets LIBLTDL to the link flags for the libltdl installable library and
+# LTDLINCL to the include flags for the libltdl header and adds
+# --enable-ltdl-install to the configure arguments.  Note that LIBLTDL
+# and LTDLINCL are not AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called.  If
+# DIRECTORY is not provided and an installed libltdl is not found, it is
+# assumed to be `libltdl'.  LIBLTDL will be prefixed with '${top_builddir}/'
+# and LTDLINCL will be prefixed with '${top_srcdir}/' (note the single
+# quotes!).  If your package is not flat and you're not using automake,
+# define top_builddir and top_srcdir appropriately in the Makefiles.
+# In the future, this macro may have to be called after AC_PROG_LIBTOOL.
+AC_DEFUN([AC_LIBLTDL_INSTALLABLE],
+[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl
+  AC_CHECK_LIB(ltdl, lt_dlinit,
+  [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no],
+  [if test x"$enable_ltdl_install" = xno; then
+     AC_MSG_WARN([libltdl not installed, but installation disabled])
+   else
+     enable_ltdl_install=yes
+   fi
+  ])
+  if test x"$enable_ltdl_install" = x"yes"; then
+    ac_configure_args="$ac_configure_args --enable-ltdl-install"
+    LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la
+    LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl'])
+  else
+    ac_configure_args="$ac_configure_args --enable-ltdl-install=no"
+    LIBLTDL="-lltdl"
+    LTDLINCL=
+  fi
+  # For backwards non-gettext consistent compatibility...
+  INCLTDL="$LTDLINCL"
+])# AC_LIBLTDL_INSTALLABLE
+
+
+# AC_LIBTOOL_CXX
+# --------------
+# enable support for C++ libraries
+AC_DEFUN([AC_LIBTOOL_CXX],
+[AC_REQUIRE([_LT_AC_LANG_CXX])
+])# AC_LIBTOOL_CXX
+
+
+# _LT_AC_LANG_CXX
+# ---------------
+AC_DEFUN([_LT_AC_LANG_CXX],
+[AC_REQUIRE([AC_PROG_CXX])
+AC_REQUIRE([AC_PROG_CXXCPP])
+_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX])
+])# _LT_AC_LANG_CXX
+
+
+# AC_LIBTOOL_F77
+# --------------
+# enable support for Fortran 77 libraries
+AC_DEFUN([AC_LIBTOOL_F77],
+[AC_REQUIRE([_LT_AC_LANG_F77])
+])# AC_LIBTOOL_F77
+
+
+# _LT_AC_LANG_F77
+# ---------------
+AC_DEFUN([_LT_AC_LANG_F77],
+[AC_REQUIRE([AC_PROG_F77])
+_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77])
+])# _LT_AC_LANG_F77
+
+
+# AC_LIBTOOL_GCJ
+# --------------
+# enable support for GCJ libraries
+AC_DEFUN([AC_LIBTOOL_GCJ],
+[AC_REQUIRE([_LT_AC_LANG_GCJ])
+])# AC_LIBTOOL_GCJ
+
+
+# _LT_AC_LANG_GCJ
+# ---------------
+AC_DEFUN([_LT_AC_LANG_GCJ],
+[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[],
+    [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[],
+      [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])],
+	 [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])],
+	   [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])])
+_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ])
+])# _LT_AC_LANG_GCJ
+
+
+# AC_LIBTOOL_RC
+# --------------
+# enable support for Windows resource files
+AC_DEFUN([AC_LIBTOOL_RC],
+[AC_REQUIRE([LT_AC_PROG_RC])
+_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC])
+])# AC_LIBTOOL_RC
+
+
+# AC_LIBTOOL_LANG_C_CONFIG
+# ------------------------
+# Ensure that the configuration vars for the C compiler are
+# suitably defined.  Those variables are subsequently used by
+# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
+AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG], [_LT_AC_LANG_C_CONFIG])
+AC_DEFUN([_LT_AC_LANG_C_CONFIG],
+[lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_AC_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}\n'
+
+_LT_AC_SYS_COMPILER
+
+#
+# Check for any special shared library compilation flags.
+#
+_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)=
+if test "$GCC" = no; then
+  case $host_os in
+  sco3.2v5*)
+    _LT_AC_TAGVAR(lt_prog_cc_shlib, $1)='-belf'
+    ;;
+  esac
+fi
+if test -n "$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)"; then
+  AC_MSG_WARN([`$CC' requires `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to build shared libraries])
+  if echo "$old_CC $old_CFLAGS " | grep "[[ 	]]$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)[[ 	]]" >/dev/null; then :
+  else
+    AC_MSG_WARN([add `$_LT_AC_TAGVAR(lt_prog_cc_shlib, $1)' to the CC or CFLAGS env variable and reconfigure])
+    _LT_AC_TAGVAR(lt_cv_prog_cc_can_build_shared, $1)=no
+  fi
+fi
+
+
+#
+# Check to make sure the static flag actually works.
+#
+AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $_LT_AC_TAGVAR(lt_prog_compiler_static, $1) works],
+  _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1),
+  $_LT_AC_TAGVAR(lt_prog_compiler_static, $1),
+  [],
+  [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=])
+
+
+AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1)
+AC_LIBTOOL_PROG_COMPILER_PIC($1)
+AC_LIBTOOL_PROG_CC_C_O($1)
+AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
+AC_LIBTOOL_PROG_LD_SHLIBS($1)
+AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
+AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
+AC_LIBTOOL_SYS_LIB_STRIP
+AC_LIBTOOL_DLOPEN_SELF($1)
+
+# Report which librarie types wil actually be built
+AC_MSG_CHECKING([if libtool supports shared libraries])
+AC_MSG_RESULT([$can_build_shared])
+
+AC_MSG_CHECKING([whether to build shared libraries])
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+
+aix4* | aix5*)
+  if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+    test "$enable_shared" = yes && enable_static=no
+  fi
+  ;;
+  darwin* | rhapsody*)
+  if test "$GCC" = yes; then
+    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+    case "$host_os" in
+    rhapsody* | darwin1.[[012]])
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	_LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[[012]])
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+      ;;
+    esac
+    output_verbose_link_cmd='echo'
+    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring'
+    _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag  -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    _LT_AC_TAGVAR(hardcode_direct, $1)=no
+    _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
+    _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+    _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
+    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+  else
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+  fi
+    ;;
+esac
+AC_MSG_RESULT([$enable_shared])
+
+AC_MSG_CHECKING([whether to build static libraries])
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+AC_MSG_RESULT([$enable_static])
+
+AC_LIBTOOL_CONFIG($1)
+
+AC_LANG_POP
+CC="$lt_save_CC"
+])# AC_LIBTOOL_LANG_C_CONFIG
+
+
+# AC_LIBTOOL_LANG_CXX_CONFIG
+# --------------------------
+# Ensure that the configuration vars for the C compiler are
+# suitably defined.  Those variables are subsequently used by
+# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
+AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)])
+AC_DEFUN([_LT_AC_LANG_CXX_CONFIG],
+[AC_LANG_PUSH(C++)
+AC_REQUIRE([AC_PROG_CXX])
+AC_REQUIRE([AC_PROG_CXXCPP])
+
+_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_AC_TAGVAR(allow_undefined_flag, $1)=
+_LT_AC_TAGVAR(always_export_symbols, $1)=no
+_LT_AC_TAGVAR(archive_expsym_cmds, $1)=
+_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_AC_TAGVAR(hardcode_direct, $1)=no
+_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_AC_TAGVAR(hardcode_minus_L, $1)=no
+_LT_AC_TAGVAR(hardcode_automatic, $1)=no
+_LT_AC_TAGVAR(module_cmds, $1)=
+_LT_AC_TAGVAR(module_expsym_cmds, $1)=
+_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_AC_TAGVAR(no_undefined_flag, $1)=
+_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Dependencies to place before and after the object being linked:
+_LT_AC_TAGVAR(predep_objects, $1)=
+_LT_AC_TAGVAR(postdep_objects, $1)=
+_LT_AC_TAGVAR(predeps, $1)=
+_LT_AC_TAGVAR(postdeps, $1)=
+_LT_AC_TAGVAR(compiler_lib_search_path, $1)=
+
+# Source file extension for C++ test sources.
+ac_ext=cc
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_AC_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_AC_SYS_COMPILER
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_LD=$LD
+lt_save_GCC=$GCC
+GCC=$GXX
+lt_save_with_gnu_ld=$with_gnu_ld
+lt_save_path_LD=$lt_cv_path_LD
+if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+  lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+else
+  unset lt_cv_prog_gnu_ld
+fi
+if test -n "${lt_cv_path_LDCXX+set}"; then
+  lt_cv_path_LD=$lt_cv_path_LDCXX
+else
+  unset lt_cv_path_LD
+fi
+test -z "${LDCXX+set}" || LD=$LDCXX
+CC=${CXX-"c++"}
+compiler=$CC
+_LT_AC_TAGVAR(compiler, $1)=$CC
+cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
+
+# We don't want -fno-exception wen compiling C++ code, so set the
+# no_builtin_flag separately
+if test "$GXX" = yes; then
+  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+else
+  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+fi
+
+if test "$GXX" = yes; then
+  # Set up default GNU C++ configuration
+
+  AC_PROG_LD
+
+  # Check if GNU C++ uses GNU ld as the underlying linker, since the
+  # archiving commands below assume that GNU ld is being used.
+  if test "$with_gnu_ld" = yes; then
+    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+    #     investigate it a little bit more. (MM)
+    wlarc='${wl}'
+
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if eval "`$CC -print-prog-name=ld` --help 2>&1" | \
+	grep 'no-whole-archive' > /dev/null; then
+      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    with_gnu_ld=no
+    wlarc=
+
+    # A generic and very simple default shared library creation
+    # command for GNU C++ for the case where it uses the native
+    # linker, instead of GNU ld.  If possible, this setting should
+    # overridden to take advantage of the native linker features on
+    # the platform it is being used on.
+    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+  fi
+
+  # Commands to make compiler produce verbose output that lists
+  # what "hidden" libraries, object files and flags are used when
+  # linking a shared library.
+  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+else
+  GXX=no
+  with_gnu_ld=no
+  wlarc=
+fi
+
+# PORTME: fill in a description of your system's C++ link characteristics
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+_LT_AC_TAGVAR(ld_shlibs, $1)=yes
+case $host_os in
+  aix3*)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  aix4* | aix5*)
+    if test "$host_cpu" = ia64; then
+      # On IA64, the linker does run time linking by default, so we don't
+      # have to do anything special.
+      aix_use_runtimelinking=no
+      exp_sym_flag='-Bexport'
+      no_entry_flag=""
+    else
+      aix_use_runtimelinking=no
+
+      # Test if we are trying to use run time linking or normal
+      # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+      # need to do runtime linking.
+      case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*)
+	for ld_flag in $LDFLAGS; do
+	  case $ld_flag in
+	  *-brtl*)
+	    aix_use_runtimelinking=yes
+	    break
+	    ;;
+	  esac
+	done
+      esac
+
+      exp_sym_flag='-bexport'
+      no_entry_flag='-bnoentry'
+    fi
+
+    # When large executables or shared objects are built, AIX ld can
+    # have problems creating the table of contents.  If linking a library
+    # or program results in "error TOC overflow" add -mminimal-toc to
+    # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+    # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+    _LT_AC_TAGVAR(archive_cmds, $1)=''
+    _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':'
+    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+
+    if test "$GXX" = yes; then
+      case $host_os in aix4.[012]|aix4.[012].*)
+      # We only want to do this on AIX 4.2 and lower, the check
+      # below for broken collect2 doesn't work under 4.3+
+	collect2name=`${CC} -print-prog-name=collect2`
+	if test -f "$collect2name" && \
+	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	then
+	  # We have reworked collect2
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+	else
+	  # We have old collect2
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
+	fi
+      esac
+      shared_flag='-shared'
+    else
+      # not using gcc
+      if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	shared_flag='-G'
+      else
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag='${wl}-G'
+	else
+	  shared_flag='${wl}-bM:SRE'
+	fi
+      fi
+    fi
+
+    # It seems that -bexpall does not export symbols beginning with
+    # underscore (_), so it is better to generate a list of symbols to export.
+    _LT_AC_TAGVAR(always_export_symbols, $1)=yes
+    if test "$aix_use_runtimelinking" = yes; then
+      # Warning - without using the other runtime loading flags (-brtl),
+      # -berok will link without error, but may produce a broken library.
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok'
+      # Determine the default libpath from the value encoded in an empty executable.
+      _LT_AC_SYS_LIBPATH_AIX
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+     else
+      if test "$host_cpu" = ia64; then
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+      else
+	# Determine the default libpath from the value encoded in an empty executable.
+	_LT_AC_SYS_LIBPATH_AIX
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	# Warning - without using the other run time loading flags,
+	# -berok will link without error, but may produce a broken library.
+	_LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	# -bexpall does not export symbols beginning with underscore (_)
+	_LT_AC_TAGVAR(always_export_symbols, $1)=yes
+	# Exported symbols can be pulled into shared objects from archives
+	_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' '
+	_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
+	# This is similar to how AIX traditionally builds it's shared libraries.
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+      fi
+    fi
+    ;;
+  chorus*)
+    case $cc_basename in
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+    # as there is no search path for DLLs.
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+    _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+    _LT_AC_TAGVAR(always_export_symbols, $1)=no
+    _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+    if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+      # If the export-symbols file already is a .def file (1st line
+      # is EXPORTS), use it as is; otherwise, prepend...
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	cp $export_symbols $output_objdir/$soname.def;
+      else
+	echo EXPORTS > $output_objdir/$soname.def;
+	cat $export_symbols >> $output_objdir/$soname.def;
+      fi~
+      $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+    else
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    fi
+  ;;
+
+  darwin* | rhapsody*)
+  if test "$GXX" = yes; then
+    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+    case "$host_os" in
+    rhapsody* | darwin1.[[012]])
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	_LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[[012]])
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+      ;;
+    esac
+    lt_int_apple_cc_single_mod=no
+    output_verbose_link_cmd='echo'
+    if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+      lt_int_apple_cc_single_mod=yes
+    fi
+    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    else
+      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    fi
+    _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+
+    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    else
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    _LT_AC_TAGVAR(hardcode_direct, $1)=no
+    _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
+    _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+    _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
+    _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+  else
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+  fi
+    ;;
+
+  dgux*)
+    case $cc_basename in
+      ec++)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      ghcx)
+	# Green Hills C++ Compiler
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+  freebsd[12]*)
+    # C++ shared libraries reported to be fairly broken before switch to ELF
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  freebsd-elf*)
+    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+    ;;
+  freebsd* | kfreebsd*-gnu)
+    # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+    # conventions
+    _LT_AC_TAGVAR(ld_shlibs, $1)=yes
+    ;;
+  gnu*)
+    ;;
+  hpux9*)
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+    _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+    _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+				# but as the default
+				# location of the library.
+
+    case $cc_basename in
+    CC)
+      # FIXME: insert proper C++ library support
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    aCC)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      #
+      # There doesn't appear to be a way to prevent this compiler from
+      # explicitly linking system object files so we need to strip them
+      # from the output so that they don't get included in the library
+      # dependencies.
+      output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[-]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+      ;;
+    *)
+      if test "$GXX" = yes; then
+        _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+        # FIXME: insert proper C++ library support
+        _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+    ;;
+  hpux10*|hpux11*)
+    if test $with_gnu_ld = no; then
+      case "$host_cpu" in
+      hppa*64*)
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+        ;;
+      ia64*)
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+        ;;
+      *)
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+	_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+        ;;
+      esac
+    fi
+    case "$host_cpu" in
+    hppa*64*)
+      _LT_AC_TAGVAR(hardcode_direct, $1)=no
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+    ia64*)
+      _LT_AC_TAGVAR(hardcode_direct, $1)=no
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+					      # but as the default
+					      # location of the library.
+      ;;
+    *)
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+					      # but as the default
+					      # location of the library.
+      ;;
+    esac
+
+    case $cc_basename in
+      CC)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      aCC)
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
+	  ;;
+	*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	  ;;
+	esac
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes; then
+	  if test $with_gnu_ld = no; then
+	    case "$host_cpu" in
+	    ia64*|hppa*64*)
+	      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
+	      ;;
+	    *)
+	      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	      ;;
+	    esac
+	  fi
+	else
+	  # FIXME: insert proper C++ library support
+	  _LT_AC_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+    esac
+    ;;
+  irix5* | irix6*)
+    case $cc_basename in
+      CC)
+	# SGI C++
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+
+	# Archives containing C++ object files must be created using
+	# "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+	# necessary to make sure instantiated templates are included
+	# in the archive.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+	;;
+      *)
+	if test "$GXX" = yes; then
+	  if test "$with_gnu_ld" = no; then
+	    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+	  else
+	    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib'
+	  fi
+	fi
+	_LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+	;;
+    esac
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+    _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+    ;;
+  linux*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	_LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir'
+	_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+	# Archives containing C++ object files must be created using
+	# "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+	;;
+      icpc)
+	# Intel C++
+	with_gnu_ld=yes
+	_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	_LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	;;
+      cxx)
+	# Compaq C++
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+	runpath_var=LD_RUN_PATH
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+    esac
+    ;;
+  lynxos*)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  m88k*)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  mvs*)
+    case $cc_basename in
+      cxx)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+  netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+      wlarc=
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+    fi
+    # Workaround some broken pre-1.5 toolchains
+    output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+    ;;
+  osf3*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	_LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	# Archives containing C++ object files must be created using
+	# "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+
+	;;
+      RCC)
+	# Rational C++ 2.4.1
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      cxx)
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	  # Commands to make compiler produce verbose output that lists
+	  # what "hidden" libraries, object files and flags are used when
+	  # linking a shared library.
+	  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+	else
+	  # FIXME: insert proper C++ library support
+	  _LT_AC_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+    esac
+    ;;
+  osf4* | osf5*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	_LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	# Archives containing C++ object files must be created using
+	# the KAI C++ compiler.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs'
+	;;
+      RCC)
+	# Rational C++ 2.4.1
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      cxx)
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+	  echo "-hidden">> $lib.exp~
+	  $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp  `test -n "$verstring" && echo -set_version	$verstring` -update_registry $objdir/so_locations -o $lib~
+	  $rm $lib.exp'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+	_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	 _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	  # Commands to make compiler produce verbose output that lists
+	  # what "hidden" libraries, object files and flags are used when
+	  # linking a shared library.
+	  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+	else
+	  # FIXME: insert proper C++ library support
+	  _LT_AC_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+    esac
+    ;;
+  psos*)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  sco*)
+    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+    case $cc_basename in
+      CC)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+  sunos4*)
+    case $cc_basename in
+      CC)
+	# Sun C++ 4.x
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      lcc)
+	# Lucid
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+  solaris*)
+    case $cc_basename in
+      CC)
+	# Sun C++ 4.2, 5.x and Centerline C++
+	_LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	$CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+	case $host_os in
+	  solaris2.[0-5] | solaris2.[0-5].*) ;;
+	  *)
+	    # The C++ compiler is used as linker so we must use $wl
+	    # flag to pass the commands to the underlying system
+	    # linker.
+	    # Supported since Solaris 2.6 (maybe 2.5.1?)
+	    _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	    ;;
+	esac
+	_LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[[LR]]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+
+	# Archives containing C++ object files must be created using
+	# "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	# necessary to make sure instantiated templates are included
+	# in the archive.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+	;;
+      gcx)
+	# Green Hills C++ Compiler
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+	# The C++ compiler must be used to create the archive.
+	_LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+	;;
+      *)
+	# GNU C++ compiler with Solaris linker
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+	  if $CC --version | grep -v '^2\.7' > /dev/null; then
+	    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
+	  else
+	    # g++ 2.7 appears to require `-G' NOT `-shared' on this
+	    # platform.
+	    _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	    _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
+	  fi
+
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+	fi
+	;;
+    esac
+    ;;
+  sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[[78]]* | unixware7*)
+    _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+    ;;
+  tandem*)
+    case $cc_basename in
+      NCC)
+	# NonStop-UX NCC 3.20
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	;;
+    esac
+    ;;
+  vxworks*)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+  *)
+    # FIXME: insert proper C++ library support
+    _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    ;;
+esac
+AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)])
+test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_AC_TAGVAR(GCC, $1)="$GXX"
+_LT_AC_TAGVAR(LD, $1)="$LD"
+
+AC_LIBTOOL_POSTDEP_PREDEP($1)
+AC_LIBTOOL_PROG_COMPILER_PIC($1)
+AC_LIBTOOL_PROG_CC_C_O($1)
+AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
+AC_LIBTOOL_PROG_LD_SHLIBS($1)
+AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
+AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
+AC_LIBTOOL_SYS_LIB_STRIP
+AC_LIBTOOL_DLOPEN_SELF($1)
+
+AC_LIBTOOL_CONFIG($1)
+
+AC_LANG_POP
+CC=$lt_save_CC
+LDCXX=$LD
+LD=$lt_save_LD
+GCC=$lt_save_GCC
+with_gnu_ldcxx=$with_gnu_ld
+with_gnu_ld=$lt_save_with_gnu_ld
+lt_cv_path_LDCXX=$lt_cv_path_LD
+lt_cv_path_LD=$lt_save_path_LD
+lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+])# AC_LIBTOOL_LANG_CXX_CONFIG
+
+# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME])
+# ------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+ifelse([$1],[],[cat > conftest.$ac_ext <<EOF
+int a;
+void foo (void) { a = 0; }
+EOF
+],[$1],[CXX],[cat > conftest.$ac_ext <<EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+EOF
+],[$1],[F77],[cat > conftest.$ac_ext <<EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+EOF
+],[$1],[GCJ],[cat > conftest.$ac_ext <<EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+EOF
+])
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  # The `*' in the case matches for architectures that use `case' in
+  # $output_verbose_cmd can trigger glob expansion during the loop
+  # eval without this substitution.
+  output_verbose_link_cmd="`$echo \"X$output_verbose_link_cmd\" | $Xsed -e \"$no_glob_subst\"`"
+
+  for p in `eval $output_verbose_link_cmd`; do
+    case $p in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" \
+	  || test $p = "-R"; then
+	 prev=$p
+	 continue
+       else
+	 prev=
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 case $p in
+	 -L* | -R*)
+	   # Internal compiler library paths should come after those
+	   # provided the user.  The postdeps already come after the
+	   # user supplied libs so there is no need to process them.
+	   if test -z "$_LT_AC_TAGVAR(compiler_lib_search_path, $1)"; then
+	     _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+	   else
+	     _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${_LT_AC_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+	   fi
+	   ;;
+	 # The "-l" case would never come before the object being
+	 # linked, so don't bother handling this case.
+	 esac
+       else
+	 if test -z "$_LT_AC_TAGVAR(postdeps, $1)"; then
+	   _LT_AC_TAGVAR(postdeps, $1)="${prev}${p}"
+	 else
+	   _LT_AC_TAGVAR(postdeps, $1)="${_LT_AC_TAGVAR(postdeps, $1)} ${prev}${p}"
+	 fi
+       fi
+       ;;
+
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+	 pre_test_object_deps_done=yes
+	 continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 if test -z "$_LT_AC_TAGVAR(predep_objects, $1)"; then
+	   _LT_AC_TAGVAR(predep_objects, $1)="$p"
+	 else
+	   _LT_AC_TAGVAR(predep_objects, $1)="$_LT_AC_TAGVAR(predep_objects, $1) $p"
+	 fi
+       else
+	 if test -z "$_LT_AC_TAGVAR(postdep_objects, $1)"; then
+	   _LT_AC_TAGVAR(postdep_objects, $1)="$p"
+	 else
+	   _LT_AC_TAGVAR(postdep_objects, $1)="$_LT_AC_TAGVAR(postdep_objects, $1) $p"
+	 fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$rm -f confest.$objext
+
+case " $_LT_AC_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+])# AC_LIBTOOL_POSTDEP_PREDEP
+
+# AC_LIBTOOL_LANG_F77_CONFIG
+# ------------------------
+# Ensure that the configuration vars for the C compiler are
+# suitably defined.  Those variables are subsequently used by
+# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
+AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG], [_LT_AC_LANG_F77_CONFIG(F77)])
+AC_DEFUN([_LT_AC_LANG_F77_CONFIG],
+[AC_REQUIRE([AC_PROG_F77])
+AC_LANG_PUSH(Fortran 77)
+
+_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_AC_TAGVAR(allow_undefined_flag, $1)=
+_LT_AC_TAGVAR(always_export_symbols, $1)=no
+_LT_AC_TAGVAR(archive_expsym_cmds, $1)=
+_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_AC_TAGVAR(hardcode_direct, $1)=no
+_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_AC_TAGVAR(hardcode_minus_L, $1)=no
+_LT_AC_TAGVAR(hardcode_automatic, $1)=no
+_LT_AC_TAGVAR(module_cmds, $1)=
+_LT_AC_TAGVAR(module_expsym_cmds, $1)=
+_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_AC_TAGVAR(no_undefined_flag, $1)=
+_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_AC_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="      subroutine t\n      return\n      end\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="      program t\n      end\n"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_AC_SYS_COMPILER
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${F77-"f77"}
+compiler=$CC
+_LT_AC_TAGVAR(compiler, $1)=$CC
+cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
+
+AC_MSG_CHECKING([if libtool supports shared libraries])
+AC_MSG_RESULT([$can_build_shared])
+
+AC_MSG_CHECKING([whether to build shared libraries])
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+aix4* | aix5*)
+  test "$enable_shared" = yes && enable_static=no
+  ;;
+esac
+AC_MSG_RESULT([$enable_shared])
+
+AC_MSG_CHECKING([whether to build static libraries])
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+AC_MSG_RESULT([$enable_static])
+
+test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_AC_TAGVAR(GCC, $1)="$G77"
+_LT_AC_TAGVAR(LD, $1)="$LD"
+
+AC_LIBTOOL_PROG_COMPILER_PIC($1)
+AC_LIBTOOL_PROG_CC_C_O($1)
+AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
+AC_LIBTOOL_PROG_LD_SHLIBS($1)
+AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
+AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
+AC_LIBTOOL_SYS_LIB_STRIP
+
+
+AC_LIBTOOL_CONFIG($1)
+
+AC_LANG_POP
+CC="$lt_save_CC"
+])# AC_LIBTOOL_LANG_F77_CONFIG
+
+
+# AC_LIBTOOL_LANG_GCJ_CONFIG
+# --------------------------
+# Ensure that the configuration vars for the C compiler are
+# suitably defined.  Those variables are subsequently used by
+# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
+AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG], [_LT_AC_LANG_GCJ_CONFIG(GCJ)])
+AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG],
+[AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_AC_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[] argv) {}; }\n'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_AC_SYS_COMPILER
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${GCJ-"gcj"}
+compiler=$CC
+_LT_AC_TAGVAR(compiler, $1)=$CC
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+
+AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1)
+AC_LIBTOOL_PROG_COMPILER_PIC($1)
+AC_LIBTOOL_PROG_CC_C_O($1)
+AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1)
+AC_LIBTOOL_PROG_LD_SHLIBS($1)
+AC_LIBTOOL_SYS_DYNAMIC_LINKER($1)
+AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1)
+AC_LIBTOOL_SYS_LIB_STRIP
+AC_LIBTOOL_DLOPEN_SELF($1)
+
+AC_LIBTOOL_CONFIG($1)
+
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# AC_LIBTOOL_LANG_GCJ_CONFIG
+
+
+# AC_LIBTOOL_LANG_RC_CONFIG
+# --------------------------
+# Ensure that the configuration vars for the Windows resource compiler are
+# suitably defined.  Those variables are subsequently used by
+# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'.
+AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG], [_LT_AC_LANG_RC_CONFIG(RC)])
+AC_DEFUN([_LT_AC_LANG_RC_CONFIG],
+[AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_AC_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_AC_SYS_COMPILER
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${RC-"windres"}
+compiler=$CC
+_LT_AC_TAGVAR(compiler, $1)=$CC
+_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+AC_LIBTOOL_CONFIG($1)
+
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# AC_LIBTOOL_LANG_RC_CONFIG
+
+
+# AC_LIBTOOL_CONFIG([TAGNAME])
+# ----------------------------
+# If TAGNAME is not passed, then create an initial libtool script
+# with a default configuration from the untagged config vars.  Otherwise
+# add code to config.status for appending the configuration named by
+# TAGNAME from the matching tagged config vars.
+AC_DEFUN([AC_LIBTOOL_CONFIG],
+[# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    _LT_AC_TAGVAR(compiler, $1) \
+    _LT_AC_TAGVAR(CC, $1) \
+    _LT_AC_TAGVAR(LD, $1) \
+    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1) \
+    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1) \
+    _LT_AC_TAGVAR(lt_prog_compiler_static, $1) \
+    _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) \
+    _LT_AC_TAGVAR(export_dynamic_flag_spec, $1) \
+    _LT_AC_TAGVAR(thread_safe_flag_spec, $1) \
+    _LT_AC_TAGVAR(whole_archive_flag_spec, $1) \
+    _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) \
+    _LT_AC_TAGVAR(old_archive_cmds, $1) \
+    _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) \
+    _LT_AC_TAGVAR(predep_objects, $1) \
+    _LT_AC_TAGVAR(postdep_objects, $1) \
+    _LT_AC_TAGVAR(predeps, $1) \
+    _LT_AC_TAGVAR(postdeps, $1) \
+    _LT_AC_TAGVAR(compiler_lib_search_path, $1) \
+    _LT_AC_TAGVAR(archive_cmds, $1) \
+    _LT_AC_TAGVAR(archive_expsym_cmds, $1) \
+    _LT_AC_TAGVAR(postinstall_cmds, $1) \
+    _LT_AC_TAGVAR(postuninstall_cmds, $1) \
+    _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) \
+    _LT_AC_TAGVAR(allow_undefined_flag, $1) \
+    _LT_AC_TAGVAR(no_undefined_flag, $1) \
+    _LT_AC_TAGVAR(export_symbols_cmds, $1) \
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) \
+    _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) \
+    _LT_AC_TAGVAR(hardcode_libdir_separator, $1) \
+    _LT_AC_TAGVAR(hardcode_automatic, $1) \
+    _LT_AC_TAGVAR(module_cmds, $1) \
+    _LT_AC_TAGVAR(module_expsym_cmds, $1) \
+    _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) \
+    _LT_AC_TAGVAR(exclude_expsyms, $1) \
+    _LT_AC_TAGVAR(include_expsyms, $1); do
+
+    case $var in
+    _LT_AC_TAGVAR(old_archive_cmds, $1) | \
+    _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) | \
+    _LT_AC_TAGVAR(archive_cmds, $1) | \
+    _LT_AC_TAGVAR(archive_expsym_cmds, $1) | \
+    _LT_AC_TAGVAR(module_cmds, $1) | \
+    _LT_AC_TAGVAR(module_expsym_cmds, $1) | \
+    _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) | \
+    _LT_AC_TAGVAR(export_symbols_cmds, $1) | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\[$]0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\[$]0 --fallback-echo"[$]/[$]0 --fallback-echo"/'`
+    ;;
+  esac
+
+ifelse([$1], [],
+  [cfgfile="${ofile}T"
+  trap "$rm \"$cfgfile\"; exit 1" 1 2 15
+  $rm -f "$cfgfile"
+  AC_MSG_NOTICE([creating $ofile])],
+  [cfgfile="$ofile"])
+
+  cat <<__EOF__ >> "$cfgfile"
+ifelse([$1], [],
+[#! $SHELL
+
+# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+#
+# This file is part of GNU Libtool:
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="$SED -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+# The names of the tagged configurations supported by this script.
+available_tags=
+
+# ### BEGIN LIBTOOL CONFIG],
+[# ### BEGIN LIBTOOL TAG CONFIG: $tagname])
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_[]_LT_AC_TAGVAR(compiler, $1)
+
+# Is the compiler the GNU C compiler?
+with_gcc=$_LT_AC_TAGVAR(GCC, $1)
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_[]_LT_AC_TAGVAR(LD, $1)
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1)
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1)
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1)
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1)
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1)
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1)
+archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1)
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1)
+module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1)
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1)
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1)
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1)
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1)
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1)
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1)
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1)
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1)
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1)
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1)
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1)
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1)
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1)
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1)
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1)
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1)
+
+# Symbols that must always be exported.
+include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1)
+
+ifelse([$1],[],
+[# ### END LIBTOOL CONFIG],
+[# ### END LIBTOOL TAG CONFIG: $tagname])
+
+__EOF__
+
+ifelse([$1],[], [
+  case $host_os in
+  aix3*)
+    cat <<\EOF >> "$cfgfile"
+
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+EOF
+    ;;
+  esac
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1)
+
+  mv -f "$cfgfile" "$ofile" || \
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+])
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+])# AC_LIBTOOL_CONFIG
+
+
+# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------------------
+AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI],
+[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl
+
+_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+  _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+
+  AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI
+
+
+# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
+# ---------------------------------
+AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE],
+[AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_NM])
+AC_REQUIRE([AC_OBJEXT])
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*) # Its linker distinguishes data from code symbols
+  if test "$host_cpu" = ia64; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+  lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris* | sysv5*)
+  symcode='[[BDRT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Write the raw and C identifiers.
+  lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ 	]]\($symcode$symcode*\)[[ 	]][[ 	]]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if grep ' nm_test_var$' "$nlist" >/dev/null; then
+	if grep ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext'
+
+	  cat <<EOF >> conftest.$ac_ext
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr_t address;
+}
+lt_preloaded_symbols[[]] =
+{
+EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext
+	  cat <<\EOF >> conftest.$ac_ext
+  {0, (lt_ptr_t) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_save_LIBS="$LIBS"
+	  lt_save_CFLAGS="$CFLAGS"
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+	  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS="$lt_save_LIBS"
+	  CFLAGS="$lt_save_CFLAGS"
+	else
+	  echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -f conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE
+
+
+# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME])
+# ---------------------------------------
+AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC],
+[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=
+
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+ ifelse([$1],[CXX],[
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+      ;;
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | os2* | pw32*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	;;
+      *)
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+    *)
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix4* | aix5*)
+	# All AIX code is PIC.
+	if test "$host_cpu" = ia64; then
+	  # AIX 5 now supports IA64 processor
+	  _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	else
+	  _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+	fi
+	;;
+      chorus*)
+	case $cc_basename in
+	cxch68)
+	  # Green Hills C++ Compiler
+	  # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+	  ;;
+	esac
+	;;
+      dgux*)
+	case $cc_basename in
+	  ec++)
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  ghcx)
+	    # Green Hills C++ Compiler
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      freebsd* | kfreebsd*-gnu)
+	# FreeBSD uses GNU C++
+	;;
+      hpux9* | hpux10* | hpux11*)
+	case $cc_basename in
+	  CC)
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
+	    if test "$host_cpu" != ia64; then
+	      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	    fi
+	    ;;
+	  aCC)
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
+	    case "$host_cpu" in
+	    hppa*64*|ia64*)
+	      # +Z the default
+	      ;;
+	    *)
+	      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	      ;;
+	    esac
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      irix5* | irix6* | nonstopux*)
+	case $cc_basename in
+	  CC)
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    # CC pic flag -KPIC is the default.
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      linux*)
+	case $cc_basename in
+	  KCC)
+	    # KAI C++ Compiler
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    ;;
+	  icpc)
+	    # Intel C++
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	    ;;
+	  cxx)
+	    # Compaq C++
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      lynxos*)
+	;;
+      m88k*)
+	;;
+      mvs*)
+	case $cc_basename in
+	  cxx)
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+	;;
+      osf3* | osf4* | osf5*)
+	case $cc_basename in
+	  KCC)
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    ;;
+	  RCC)
+	    # Rational C++ 2.4.1
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  cxx)
+	    # Digital/Compaq C++
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      psos*)
+	;;
+      sco*)
+	case $cc_basename in
+	  CC)
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      solaris*)
+	case $cc_basename in
+	  CC)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	    ;;
+	  gcx)
+	    # Green Hills C++ Compiler
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sunos4*)
+	case $cc_basename in
+	  CC)
+	    # Sun C++ 4.x
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	  lcc)
+	    # Lucid
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      tandem*)
+	case $cc_basename in
+	  NCC)
+	    # NonStop-UX NCC 3.20
+	    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      unixware*)
+	;;
+      vxworks*)
+	;;
+      *)
+	_LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+	;;
+    esac
+  fi
+],
+[
+  if test "$GCC" = yes; then
+    _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+      ;;
+
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+
+    *)
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    newsos6)
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    linux*)
+      case $CC in
+      icc* | ecc*)
+	_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      ccc*)
+        _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      esac
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kpic'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-dn'
+      ;;
+
+    solaris*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sunos4*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+	_LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    uts4*)
+      _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)])
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works],
+    _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1),
+    [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+case "$host_os" in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+])
+
+
+# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME])
+# ------------------------------------
+# See if the linker supports building shared libraries.
+AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS],
+[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ifelse([$1],[CXX],[
+  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  case $host_os in
+  aix4* | aix5*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+  ;;
+  cygwin* | mingw*)
+    _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  linux*)
+    _LT_AC_TAGVAR(link_all_deplibs, $1)=no
+  ;;
+  *)
+    _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  esac
+],[
+  runpath_var=
+  _LT_AC_TAGVAR(allow_undefined_flag, $1)=
+  _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_AC_TAGVAR(archive_cmds, $1)=
+  _LT_AC_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)=
+  _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
+  _LT_AC_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_AC_TAGVAR(hardcode_direct, $1)=no
+  _LT_AC_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_AC_TAGVAR(hardcode_automatic, $1)=no
+  _LT_AC_TAGVAR(module_cmds, $1)=
+  _LT_AC_TAGVAR(module_expsym_cmds, $1)=
+  _LT_AC_TAGVAR(always_export_symbols, $1)=no
+  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_AC_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_"
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  _LT_AC_TAGVAR(ld_shlibs, $1)=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+      fi
+      ;;
+
+    amigaos*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+
+      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we can't use
+      # them.
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_AC_TAGVAR(always_export_symbols, $1)=no
+      _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000  ${wl}--out-implib,$lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris* | sysv5*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+	cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sunos4*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+  linux*)
+    if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_cmds, $1)="$tmp_archive_cmds"
+      supports_anon_versioning=no
+      case `$LD -v 2>/dev/null` in
+        *\ [01].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+        *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+        *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+        *\ 2.11.*) ;; # other 2.11 versions
+        *) supports_anon_versioning=yes ;;
+      esac
+      if test $supports_anon_versioning = yes; then
+        _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $output_objdir/$libname.ver~
+cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+$echo "local: *; };" >> $output_objdir/$libname.ver~
+        $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+      else
+        _LT_AC_TAGVAR(archive_expsym_cmds, $1)="$tmp_archive_cmds"
+      fi
+      _LT_AC_TAGVAR(link_all_deplibs, $1)=no
+    else
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    fi
+    ;;
+
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_AC_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = yes; then
+      runpath_var=LD_RUN_PATH
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then
+ 	_LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+  	_LT_AC_TAGVAR(whole_archive_flag_spec, $1)=
+      fi
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_AC_TAGVAR(always_export_symbols, $1)=yes
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      if test "$GCC" = yes && test -z "$link_static_flag"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	_LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix4* | aix5*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+	  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
+	else
+	  _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*)
+	  for ld_flag in $LDFLAGS; do
+  	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+  	    aix_use_runtimelinking=yes
+  	    break
+  	  fi
+	  done
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_AC_TAGVAR(archive_cmds, $1)=''
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.[012]|aix4.[012].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" && \
+  	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	  then
+  	  # We have reworked collect2
+  	  _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+	  else
+  	  # We have old collect2
+  	  _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported
+  	  # It fails to find uninstalled libraries when the uninstalled
+  	  # path is not listed in the libpath.  Setting hardcode_minus_L
+  	  # to unsupported forces relinking
+  	  _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+  	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+  	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=
+	  fi
+	esac
+	shared_flag='-shared'
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+  	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+  	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+  	if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+  	fi
+	fi
+      fi
+
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      _LT_AC_TAGVAR(always_export_symbols, $1)=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok'
+       # Determine the default libpath from the value encoded in an empty executable.
+       _LT_AC_SYS_LIBPATH_AIX
+       _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+       else
+	if test "$host_cpu" = ia64; then
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	  _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	  _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an empty executable.
+	 _LT_AC_SYS_LIBPATH_AIX
+	 _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	  _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	  # -bexpall does not export symbols beginning with underscore (_)
+	  _LT_AC_TAGVAR(always_export_symbols, $1)=yes
+	  # Exported symbols can be pulled into shared objects from archives
+	  _LT_AC_TAGVAR(whole_archive_flag_spec, $1)=' '
+	  _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
+	  # This is similar to how AIX traditionally builds it's shared libraries.
+	  _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      # see comment about different semantics on the GNU ld section
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      ;;
+
+    bsdi4*)
+      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true'
+      # FIXME: Should let the user specify the lib program.
+      _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs'
+      fix_srcfile_path='`cygpath -w "$srcfile"`'
+      _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      ;;
+
+    darwin* | rhapsody*)
+    if test "$GXX" = yes ; then
+      _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+      case "$host_os" in
+      rhapsody* | darwin1.[[012]])
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined suppress'
+	;;
+      *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	_LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[[012]])
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            _LT_AC_TAGVAR(allow_undefined_flag, $1)='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+	;;
+      esac
+    	lt_int_apple_cc_single_mod=no
+    	output_verbose_link_cmd='echo'
+    	if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+    	  lt_int_apple_cc_single_mod=yes
+    	fi
+    	if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+    	  _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    	else
+        _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+      fi
+      _LT_AC_TAGVAR(module_cmds, $1)='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+      # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+        if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+          _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        else
+          _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        fi
+          _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=no
+      _LT_AC_TAGVAR(hardcode_automatic, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+      _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-all_load $convenience'
+      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+    else
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+    fi
+      ;;
+
+    dgux*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    freebsd1*)
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | kfreebsd*-gnu)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      ;;
+
+    hpux10* | hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	*)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*)
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=no
+	  _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+	  ;;
+	ia64*)
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=no
+	  _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+	  ;;
+	*)
+	  _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	  _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    openbsd*)
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      else
+       case $host_os in
+	 openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+	   _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	   _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	   ;;
+	 *)
+	   _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	   _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	   ;;
+       esac
+      fi
+      ;;
+
+    os2*)
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      else
+	_LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+	$LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    sco3.2v5*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ;;
+
+    solaris*)
+      _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text'
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	  $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp'
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	_LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  	$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+	_LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;;
+      esac
+      _LT_AC_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=no
+        ;;
+	motorola)
+	  _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	_LT_AC_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4.2uw2*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_direct, $1)=yes
+      _LT_AC_TAGVAR(hardcode_minus_L, $1)=no
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      hardcode_runpath_var=yes
+      runpath_var=LD_RUN_PATH
+      ;;
+
+   sysv5OpenUNIX8* | sysv5UnixWare7* |  sysv5uw[[78]]* | unixware7*)
+      _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z ${wl}text'
+      if test "$GCC" = yes; then
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      runpath_var='LD_RUN_PATH'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv5*)
+      _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text'
+      # $CC -shared without GNU ld will not create a library from C++
+      # object files and a static libstdc++, better avoid it by now
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+      ;;
+
+    uts4*)
+      _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_AC_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+  fi
+])
+AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)])
+test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $_LT_AC_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+      $rm conftest*
+      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1)
+        _LT_AC_TAGVAR(allow_undefined_flag, $1)=
+        if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1)
+        then
+	  _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no
+        else
+	  _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes
+        fi
+        _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $rm conftest*
+      AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)])
+      ;;
+    esac
+  fi
+  ;;
+esac
+])# AC_LIBTOOL_PROG_LD_SHLIBS
+
+
+# _LT_AC_FILE_LTDLL_C
+# -------------------
+# Be careful that the start marker always follows a newline.
+AC_DEFUN([_LT_AC_FILE_LTDLL_C], [
+# /* ltdll.c starts here */
+# #define WIN32_LEAN_AND_MEAN
+# #include <windows.h>
+# #undef WIN32_LEAN_AND_MEAN
+# #include <stdio.h>
+#
+# #ifndef __CYGWIN__
+# #  ifdef __CYGWIN32__
+# #    define __CYGWIN__ __CYGWIN32__
+# #  endif
+# #endif
+#
+# #ifdef __cplusplus
+# extern "C" {
+# #endif
+# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved);
+# #ifdef __cplusplus
+# }
+# #endif
+#
+# #ifdef __CYGWIN__
+# #include <cygwin/cygwin_dll.h>
+# DECLARE_CYGWIN_DLL( DllMain );
+# #endif
+# HINSTANCE __hDllInstance_base;
+#
+# BOOL APIENTRY
+# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
+# {
+#   __hDllInstance_base = hInst;
+#   return TRUE;
+# }
+# /* ltdll.c ends here */
+])# _LT_AC_FILE_LTDLL_C
+
+
+# _LT_AC_TAGVAR(VARNAME, [TAGNAME])
+# ---------------------------------
+AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])])
+
+
+# old names
+AC_DEFUN([AM_PROG_LIBTOOL],   [AC_PROG_LIBTOOL])
+AC_DEFUN([AM_ENABLE_SHARED],  [AC_ENABLE_SHARED($@)])
+AC_DEFUN([AM_ENABLE_STATIC],  [AC_ENABLE_STATIC($@)])
+AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+AC_DEFUN([AM_PROG_LD],        [AC_PROG_LD])
+AC_DEFUN([AM_PROG_NM],        [AC_PROG_NM])
+
+# This is just to silence aclocal about the macro not being used
+ifelse([AC_DISABLE_FAST_INSTALL])
+
+AC_DEFUN([LT_AC_PROG_GCJ],
+[AC_CHECK_TOOL(GCJ, gcj, no)
+  test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+  AC_SUBST(GCJFLAGS)
+])
+
+AC_DEFUN([LT_AC_PROG_RC],
+[AC_CHECK_TOOL(RC, windres, no)
+])
+
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+# LT_AC_PROG_SED
+# --------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+AC_DEFUN([LT_AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && break
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+SED=$lt_cv_path_SED
+])
+AC_MSG_RESULT([$SED])
+])
+
+#                                                        -*- Autoconf -*-
+# Copyright (C) 2002, 2003  Free Software Foundation, Inc.
+# Generated from amversion.in; do not edit by hand.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION so it can be traced.
+# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+	 [AM_AUTOMAKE_VERSION([1.9.3])])
+
+# AM_AUX_DIR_EXPAND
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL                                              -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 6
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+	[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])
+AC_SUBST([$1_FALSE])
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# serial 7						-*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])
+])
+
+# Generate code to set up dependency tracking.   -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+#   Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#serial 2
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[for mf in $CONFIG_FILES; do
+  # Strip MF so we end up with the name of the file.
+  mf=`echo "$mf" | sed -e 's/:.*$//'`
+  # Check whether this is an Automake generated Makefile or not.
+  # We used to match only the files named `Makefile.in', but
+  # some people rename them; so instead we look at the file content.
+  # Grep'ing the first line is not enough: some people post-process
+  # each Makefile.in and add a new line on top of each file to say so.
+  # So let's grep whole file.
+  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
+    dirpart=`AS_DIRNAME("$mf")`
+  else
+    continue
+  fi
+  # Extract the definition of DEPDIR, am__include, and am__quote
+  # from the Makefile without running `make'.
+  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  am__include=`sed -n 's/^am__include = //p' < "$mf"`
+  test -z "am__include" && continue
+  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n 's/^U = //p' < "$mf"`
+  # Find all dependency output files, they are included files with
+  # $(DEPDIR) in their names.  We invoke sed twice because it is the
+  # simplest approach to changing $(DEPDIR) to its actual value in the
+  # expansion.
+  for file in `sed -n "
+    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`AS_DIRNAME(["$file"])`
+    AS_MKDIR_P([$dirpart/$fdir])
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake.                            -*- Autoconf -*-
+
+# This macro actually does too much some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 11
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.58])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+# test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+              [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+	      		     [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+                  [_AM_DEPENDENCIES(CC)],
+                  [define([AC_PROG_CC],
+                          defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+                  [_AM_DEPENDENCIES(CXX)],
+                  [define([AC_PROG_CXX],
+                          defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+])
+])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $1 | $1:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+#                                                          -*- Autoconf -*-
+# Copyright (C) 2003  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 1
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure.
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+AC_DEFUN([AM_MAINTAINER_MODE],
+[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode is disabled by default
+  AC_ARG_ENABLE(maintainer-mode,
+[  --enable-maintainer-mode  enable make rules and dependencies not useful
+			  (and sometimes confusing) to the casual installer],
+      USE_MAINTAINER_MODE=$enableval,
+      USE_MAINTAINER_MODE=no)
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST(MAINT)dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes.	-*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 2
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote="\""
+      _am_result=BSD
+   fi
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+#  -*- Autoconf -*-
+
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
+
+# Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
+# created by `make install' are always world readable, even if the
+# installer happens to have an overly restrictive umask (e.g. 077).
+# This was a mistake.  There are at least two reasons why we must not
+# use `-m 0755':
+#   - it causes special bits like SGID to be ignored,
+#   - it may be too restrictive (some setups expect 775 directories).
+#
+# Do not use -m 0755 and let people choose whatever they expect by
+# setting umask.
+#
+# We cannot accept any implementation of `mkdir' that recognizes `-p'.
+# Some implementations (such as Solaris 8's) are not thread-safe: if a
+# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
+# concurrently, both version can detect that a/ is missing, but only
+# one can create it and the other will error out.  Consequently we
+# restrict ourselves to GNU make (using the --version option ensures
+# this.)
+AC_DEFUN([AM_PROG_MKDIR_P],
+[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+  # We used to keeping the `.' as first argument, in order to
+  # allow $(mkdir_p) to be used without argument.  As in
+  #   $(mkdir_p) $(somedir)
+  # where $(somedir) is conditionally defined.  However this is wrong
+  # for two reasons:
+  #  1. if the package is installed by a user who cannot write `.'
+  #     make install will fail,
+  #  2. the above comment should most certainly read
+  #     $(mkdir_p) $(DESTDIR)$(somedir)
+  #     so it does not work when $(somedir) is undefined and
+  #     $(DESTDIR) is not.
+  #  To support the latter case, we have to write
+  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
+  #  so the `.' trick is pointless.
+  mkdir_p='mkdir -p --'
+else
+  # On NextStep and OpenStep, the `mkdir' command does not
+  # recognize any option.  It will interpret all options as
+  # directories to create, and then abort because `.' already
+  # exists.
+  for d in ./-p ./--version;
+  do
+    test -d $d && rmdir $d
+  done
+  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
+  if test -f "$ac_aux_dir/mkinstalldirs"; then
+    mkdir_p='$(mkinstalldirs)'
+  else
+    mkdir_p='$(install_sh) -d'
+  fi
+fi
+AC_SUBST([mkdir_p])])
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 2
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# AM_PROG_INSTALL_STRIP
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 1
+
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+     [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+     [m4_case([$1], [ustar],, [pax],,
+              [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+  case $_am_tool in
+  gnutar)
+    for _am_tar in tar gnutar gtar;
+    do
+      AM_RUN_LOG([$_am_tar --version]) && break
+    done
+    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+    am__untar="$_am_tar -xf -"
+    ;;
+  plaintar)
+    # Must skip GNU tar: if it does not support --format= it doesn't create
+    # ustar tarball either.
+    (tar --version) >/dev/null 2>&1 && continue
+    am__tar='tar chf - "$$tardir"'
+    am__tar_='tar chf - "$tardir"'
+    am__untar='tar xf -'
+    ;;
+  pax)
+    am__tar='pax -L -x $1 -w "$$tardir"'
+    am__tar_='pax -L -x $1 -w "$tardir"'
+    am__untar='pax -r'
+    ;;
+  cpio)
+    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+    am__untar='cpio -i -H $1 -d'
+    ;;
+  none)
+    am__tar=false
+    am__tar_=false
+    am__untar=false
+    ;;
+  esac
+
+  # If the value was cached, stop now.  We just wanted to have am__tar
+  # and am__untar set.
+  test -n "${am_cv_prog_tar_$1}" && break
+
+  # tar/untar a dummy directory, and stop if the command works
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  echo GrepMe > conftest.dir/file
+  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+  rm -rf conftest.dir
+  if test -s conftest.tar; then
+    AM_RUN_LOG([$am__untar <conftest.tar])
+    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+  fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/userspace/libebtc/autogen.sh b/userspace/libebtc/autogen.sh
new file mode 100755
index 0000000..2d50b40
--- /dev/null
+++ b/userspace/libebtc/autogen.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+echo -en "\033[31mrunning aclocal...\033[0m\n\n"
+aclocal
+echo -en "\n\n"
+
+echo -en "\033[31mrunning autoheader...\033[0m\n\n"
+autoheader
+echo -en "\n\n"
+
+echo -en "\033[31mrunning autoconf...\033[0m\n\n"
+autoconf
+echo -en "\n\n"
+
+echo -en "\033[31mrunning libtoolize...\033[0m\n\n"
+libtoolize --automake
+echo -en "\n\n"
+
+echo -en "\033[31mrunning automake...\033[0m\n\n"
+automake -f -c -a
+echo -en "\n\n"
+
+echo -en "\033[31mconfigure\033[0m\n\n"
+./configure "$@"
diff --git a/userspace/libebtc/config.guess b/userspace/libebtc/config.guess
new file mode 100755
index 0000000..c28419d
--- /dev/null
+++ b/userspace/libebtc/config.guess
@@ -0,0 +1,1450 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-10-25'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	case "${UNAME_MACHINE_ARCH}" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE_ARCH}" in
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "${UNAME_VERSION}" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    amd64:OpenBSD:*:*)
+	echo x86_64-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    cats:OpenBSD:*:*)
+	echo arm-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    luna88k:OpenBSD:*:*)
+    	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    macppc:OpenBSD:*:*)
+	echo powerpc-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvmeppc:OpenBSD:*:*)
+	echo powerpc-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mips64-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sun3:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:ekkoBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+	exit 0 ;;
+    macppc:MirBSD:*:*)
+	echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:MirBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+	        UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE="alphaev5" ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE="alphaev56" ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE="alphapca56" ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE="alphapca57" ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE="alphaev6" ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE="alphaev67" ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE="alphaev69" ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE="alphaev7" ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE="alphaev79" ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-morphos
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    DRS?6000:unix:4.0:6*)
+	echo sparc-icl-nx6
+	exit 0 ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7 && exit 0 ;;
+	esac ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    m68k:machten:*:*)
+	echo m68k-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c \
+	  && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && exit 0
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit 0 ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		$CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+		fi
+		if [ "${HP_ARCH}" = "" ]; then
+		    eval $set_cc_for_build
+		    sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ ${HP_ARCH} = "hppa2.0w" ]
+	then
+	    # avoid double evaluation of $set_cc_for_build
+	    test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+	    then
+		HP_ARCH="hppa2.0w"
+	    else
+		HP_ARCH="hppa64"
+	    fi
+	fi
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    *:UNICOS/mp:*:*)
+	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    x86:Interix*:[34]*)
+	echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+	exit 0 ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+	echo i${UNAME_MACHINE}-pc-mks
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i586-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	# the GNU system
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    cris:Linux:*:*)
+	echo cris-axis-linux-gnu
+	exit 0 ;;
+    crisv32:Linux:*:*)
+	echo crisv32-axis-linux-gnu
+	exit 0 ;;
+    frv:Linux:*:*)
+    	echo frv-unknown-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    m32r*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mipsel
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+	test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+	;;
+    mips64:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips64
+	#undef mips64el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mips64el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips64
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+	test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh64*:Linux:*:*)
+    	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	# Set LC_ALL=C to ensure ld outputs messages in English.
+	ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <features.h>
+	#ifdef __ELF__
+	# ifdef __GLIBC__
+	#  if __GLIBC__ >= 2
+	LIBC=gnu
+	#  else
+	LIBC=gnulibc1
+	#  endif
+	# else
+	LIBC=gnulibc1
+	# endif
+	#else
+	#ifdef __INTEL_COMPILER
+	LIBC=gnu
+	#else
+	LIBC=gnuaout
+	#endif
+	#endif
+	#ifdef __dietlibc__
+	LIBC=dietlibc
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+	test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit 0 ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit 0 ;;
+	i*86:syllable:*:*)
+	echo ${UNAME_MACHINE}-pc-syllable
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit 0 ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit 0 ;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+	case $UNAME_PROCESSOR in
+	    *86) UNAME_PROCESSOR=i686 ;;
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = "x86"; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+	exit 0 ;;
+    *:DragonFly:*:*)
+	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    *:*VMS:*:*)
+    	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case "${UNAME_MACHINE}" in
+	    A*) echo alpha-dec-vms && exit 0 ;;
+	    I*) echo ia64-dec-vms && exit 0 ;;
+	    V*) echo vax-dec-vms && exit 0 ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	echo i386-pc-xenix
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/userspace/libebtc/config.sub b/userspace/libebtc/config.sub
new file mode 100755
index 0000000..edb6b66
--- /dev/null
+++ b/userspace/libebtc/config.sub
@@ -0,0 +1,1555 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-08-29'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+  kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis | -knuth | -cray)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+	| am33_2.0 \
+	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+	| c4x | clipper \
+	| d10v | d30v | dlx | dsp16xx \
+	| fr30 | frv \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| i370 | i860 | i960 | ia64 \
+	| ip2k | iq2000 \
+	| m32r | m32rle | m68000 | m68k | m88k | mcore \
+	| mips | mipsbe | mipseb | mipsel | mipsle \
+	| mips16 \
+	| mips64 | mips64el \
+	| mips64vr | mips64vrel \
+	| mips64orion | mips64orionel \
+	| mips64vr4100 | mips64vr4100el \
+	| mips64vr4300 | mips64vr4300el \
+	| mips64vr5000 | mips64vr5000el \
+	| mipsisa32 | mipsisa32el \
+	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa64 | mipsisa64el \
+	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64sb1 | mipsisa64sb1el \
+	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipstx39 | mipstx39el \
+	| mn10200 | mn10300 \
+	| msp430 \
+	| ns16k | ns32k \
+	| openrisc | or32 \
+	| pdp10 | pdp11 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+	| pyramid \
+	| sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+	| sh64 | sh64le \
+	| sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
+	| strongarm \
+	| tahoe | thumb | tic4x | tic80 | tron \
+	| v850 | v850e \
+	| we32k \
+	| x86 | xscale | xstormy16 | xtensa \
+	| z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+	| avr-* \
+	| bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+	| clipper-* | craynv-* | cydra-* \
+	| d10v-* | d30v-* | dlx-* \
+	| elxsi-* \
+	| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| i*86-* | i860-* | i960-* | ia64-* \
+	| ip2k-* | iq2000-* \
+	| m32r-* | m32rle-* \
+	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | mcore-* \
+	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+	| mips16-* \
+	| mips64-* | mips64el-* \
+	| mips64vr-* | mips64vrel-* \
+	| mips64orion-* | mips64orionel-* \
+	| mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* \
+	| mips64vr5000-* | mips64vr5000el-* \
+	| mipsisa32-* | mipsisa32el-* \
+	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa64-* | mipsisa64el-* \
+	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64sb1-* | mipsisa64sb1el-* \
+	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipstx39-* | mipstx39el-* \
+	| mmix-* \
+	| msp430-* \
+	| none-* | np1-* | ns16k-* | ns32k-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+	| pyramid-* \
+	| romp-* | rs6000-* \
+	| sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+	| sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+	| tahoe-* | thumb-* \
+	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tron-* \
+	| v850-* | v850e-* | vax-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+	| xtensa-* \
+	| ymp-* \
+	| z8k-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+    	abacus)
+		basic_machine=abacus-unknown
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amd64)
+		basic_machine=x86_64-pc
+		;;
+	amd64-*)
+		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	c90)
+		basic_machine=c90-cray
+		os=-unicos
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | j90)
+		basic_machine=j90-cray
+		os=-unicos
+		;;
+	craynv)
+		basic_machine=craynv-cray
+		os=-unicosmp
+		;;
+	cr16c)
+		basic_machine=cr16c-unknown
+		os=-elf
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	crisv32 | crisv32-* | etraxfs*)
+		basic_machine=crisv32-axis
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	crx)
+		basic_machine=crx-unknown
+		os=-elf
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	decsystem10* | dec10*)
+		basic_machine=pdp10-dec
+		os=-tops10
+		;;
+	decsystem20* | dec20*)
+		basic_machine=pdp10-dec
+		os=-tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	morphos)
+		basic_machine=powerpc-unknown
+		os=-morphos
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	or32 | or32-*)
+		basic_machine=or32-unknown
+		os=-coff
+		;;
+	os400)
+		basic_machine=powerpc-ibm
+		os=-os400
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+	pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen | viac3)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon | athlon_*)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2 | pentiumiii | pentium3)
+		basic_machine=i686-pc
+		;;
+	pentium4)
+		basic_machine=i786-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentium4-*)
+		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+		;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+		;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+		;;
+	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+		basic_machine=powerpc64le-unknown
+		;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	s390 | s390-*)
+		basic_machine=s390-ibm
+		;;
+	s390x | s390x-*)
+		basic_machine=s390x-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sb1)
+		basic_machine=mipsisa64sb1-unknown
+		;;
+	sb1el)
+		basic_machine=mipsisa64sb1el-unknown
+		;;
+	sei)
+		basic_machine=mips-sei
+		os=-seiux
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sh64)
+		basic_machine=sh64-unknown
+		;;
+	sparclite-wrs | simso-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=alphaev5-cray
+		os=-unicos
+		;;
+	t90)
+		basic_machine=t90-cray
+		os=-unicos
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tic55x | c55x*)
+		basic_machine=tic55x-unknown
+		os=-coff
+		;;
+	tic6x | c6x*)
+		basic_machine=tic6x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	toad1)
+		basic_machine=pdp10-xkl
+		os=-tops20
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	tpf)
+		basic_machine=s390x-ibm
+		os=-tpf
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+		basic_machine=f301-fujitsu
+		;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	mmix)
+		basic_machine=mmix-knuth
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+		basic_machine=sh-unknown
+		;;
+	sh64)
+		basic_machine=sh64-unknown
+		;;
+	sparc | sparcv8 | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+	cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto-qnx*)
+		;;
+	-nto*)
+		os=`echo $os | sed -e 's|nto|nto-qnx|'`
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux-dietlibc)
+		os=-linux-dietlibc
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+        -os400*)
+		os=-os400
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-atheos*)
+		os=-atheos
+		;;
+	-syllable*)
+		os=-syllable
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-nova*)
+		os=-rtmk-nova
+		;;
+	-ns2 )
+		os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+        -tpf*)
+		os=-tpf
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+		os=-mint
+		;;
+	-aros*)
+		os=-aros
+		;;
+	-kaos*)
+		os=-kaos
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+    c4x-* | tic4x-*)
+        os=-coff
+        ;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=-tops20
+		;;
+	pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	or32-*)
+		os=-coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+    	*-knuth)
+		os=-mmixware
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+	*-gould)
+		os=-sysv
+		;;
+	*-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+	*-sgi)
+		os=-irix
+		;;
+	*-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-os400*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-tpf*)
+				vendor=ibm
+				;;
+			-vxsim* | -vxworks* | -windiss*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/userspace/libebtc/configure b/userspace/libebtc/configure
new file mode 100755
index 0000000..6aefeef
--- /dev/null
+++ b/userspace/libebtc/configure
@@ -0,0 +1,21154 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for EBTC 0.1.0.
+#
+# Report bugs to <libebtc@bugreport.1in1.de>.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$ECHO in
+X*--fallback-echo)
+  # Remove one level of quotation (which was required for Make).
+  ECHO=`echo "$ECHO" | sed 's,\\\\\$\\$0,'$0','`
+  ;;
+esac
+
+echo=${ECHO-echo}
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell.
+  exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+if test -z "$ECHO"; then
+if test "X${echo_test_string+set}" != Xset; then
+# find a string as large as possible, as long as the shell can cope with it
+  for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+    # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+    if (echo_test_string="`eval $cmd`") 2>/dev/null &&
+       echo_test_string="`eval $cmd`" &&
+       (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null
+    then
+      break
+    fi
+  done
+fi
+
+if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+   echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+   test "X$echo_testing_string" = "X$echo_test_string"; then
+  :
+else
+  # The Solaris, AIX, and Digital Unix default echo programs unquote
+  # backslashes.  This makes it impossible to quote backslashes using
+  #   echo "$something" | sed 's/\\/\\\\/g'
+  #
+  # So, first we look for a working echo in the user's PATH.
+
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for dir in $PATH /usr/ucb; do
+    IFS="$lt_save_ifs"
+    if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+       test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      echo="$dir/echo"
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+
+  if test "X$echo" = Xecho; then
+    # We didn't find a better echo, so look for alternatives.
+    if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' &&
+       echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` &&
+       test "X$echo_testing_string" = "X$echo_test_string"; then
+      # This shell has a builtin print -r that does the trick.
+      echo='print -r'
+    elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) &&
+	 test "X$CONFIG_SHELL" != X/bin/ksh; then
+      # If we have ksh, try running configure again with it.
+      ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+      export ORIGINAL_CONFIG_SHELL
+      CONFIG_SHELL=/bin/ksh
+      export CONFIG_SHELL
+      exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+    else
+      # Try using printf.
+      echo='printf %s\n'
+      if test "X`($echo '\t') 2>/dev/null`" = 'X\t' &&
+	 echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` &&
+	 test "X$echo_testing_string" = "X$echo_test_string"; then
+	# Cool, printf works
+	:
+      elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+	export CONFIG_SHELL
+	SHELL="$CONFIG_SHELL"
+	export SHELL
+	echo="$CONFIG_SHELL $0 --fallback-echo"
+      elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+	   test "X$echo_testing_string" = 'X\t' &&
+	   echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+	   test "X$echo_testing_string" = "X$echo_test_string"; then
+	echo="$CONFIG_SHELL $0 --fallback-echo"
+      else
+	# maybe with a smaller string...
+	prev=:
+
+	for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+	  if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null
+	  then
+	    break
+	  fi
+	  prev="$cmd"
+	done
+
+	if test "$prev" != 'sed 50q "$0"'; then
+	  echo_test_string=`eval $prev`
+	  export echo_test_string
+	  exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+	else
+	  # Oops.  We lost completely, so just stick with echo.
+	  echo=echo
+	fi
+      fi
+    fi
+  fi
+fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+ECHO=$echo
+if test "X$ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+   ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+tagnames=${tagnames+${tagnames},}CXX
+
+tagnames=${tagnames+${tagnames},}F77
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='EBTC'
+PACKAGE_TARNAME='ebtc'
+PACKAGE_VERSION='0.1.0'
+PACKAGE_STRING='EBTC 0.1.0'
+PACKAGE_BUGREPORT='libebtc@bugreport.1in1.de'
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar EBTC_LT_CURRENT EBTC_LT_REVISION EBTC_LT_AGE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP LN_S EGREP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT build build_cpu build_vendor build_os host host_cpu host_vendor host_os ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+	      localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$0" : 'X\(//\)[^/]' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+ac_env_CXX_set=${CXX+set}
+ac_env_CXX_value=$CXX
+ac_cv_env_CXX_set=${CXX+set}
+ac_cv_env_CXX_value=$CXX
+ac_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_env_CXXFLAGS_value=$CXXFLAGS
+ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set}
+ac_cv_env_CXXFLAGS_value=$CXXFLAGS
+ac_env_CXXCPP_set=${CXXCPP+set}
+ac_env_CXXCPP_value=$CXXCPP
+ac_cv_env_CXXCPP_set=${CXXCPP+set}
+ac_cv_env_CXXCPP_value=$CXXCPP
+ac_env_F77_set=${F77+set}
+ac_env_F77_value=$F77
+ac_cv_env_F77_set=${F77+set}
+ac_cv_env_F77_value=$F77
+ac_env_FFLAGS_set=${FFLAGS+set}
+ac_env_FFLAGS_value=$FFLAGS
+ac_cv_env_FFLAGS_set=${FFLAGS+set}
+ac_cv_env_FFLAGS_value=$FFLAGS
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures EBTC 0.1.0 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+			  [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+			  [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of EBTC 0.1.0:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors
+  --enable-maintainer-mode  enable make rules and dependencies not useful
+			  (and sometimes confusing) to the casual installer
+  --enable-shared[=PKGS]
+                          build shared libraries [default=yes]
+  --enable-static[=PKGS]
+                          build static libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-pic              try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-tags[=TAGS]
+                          include additional configurations [automatic]
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  CXXCPP      C++ preprocessor
+  F77         Fortran 77 compiler command
+  FFLAGS      Fortran 77 compiler flags
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <libebtc@bugreport.1in1.de>.
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+	   test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd "$ac_popdir"
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+EBTC configure 0.1.0
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by EBTC $as_me 0.1.0, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+	"s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=$`echo $ac_var`
+	echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+	       sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	{ echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	{ echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+	{ echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+	ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+am__api_version="1.9"
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	    break 3
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking whether build environment is sane" >&5
+echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$*" != "X $srcdir/configure conftest.file" \
+      && test "$*" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      { { echo "$as_me:$LINENO: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&5
+echo "$as_me: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
+Check your system clock" >&5
+echo "$as_me: error: newly created file is older than distributed files!
+Check your system clock" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,$program_prefix,;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$,$program_suffix,;$program_transform_name"
+# Double any \ or $.  echo might interpret backslashes.
+# By default was `s,x,x', remove it if useless.
+cat <<\_ACEOF >conftest.sed
+s/[\\$]/&&/g;s/;s,x,x,$//
+_ACEOF
+program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
+rm conftest.sed
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
+echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+  # We used to keeping the `.' as first argument, in order to
+  # allow $(mkdir_p) to be used without argument.  As in
+  #   $(mkdir_p) $(somedir)
+  # where $(somedir) is conditionally defined.  However this is wrong
+  # for two reasons:
+  #  1. if the package is installed by a user who cannot write `.'
+  #     make install will fail,
+  #  2. the above comment should most certainly read
+  #     $(mkdir_p) $(DESTDIR)$(somedir)
+  #     so it does not work when $(somedir) is undefined and
+  #     $(DESTDIR) is not.
+  #  To support the latter case, we have to write
+  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
+  #  so the `.' trick is pointless.
+  mkdir_p='mkdir -p --'
+else
+  # On NextStep and OpenStep, the `mkdir' command does not
+  # recognize any option.  It will interpret all options as
+  # directories to create, and then abort because `.' already
+  # exists.
+  for d in ./-p ./--version;
+  do
+    test -d $d && rmdir $d
+  done
+  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
+  if test -f "$ac_aux_dir/mkinstalldirs"; then
+    mkdir_p='$(mkinstalldirs)'
+  else
+    mkdir_p='$(install_sh) -d'
+  fi
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$AWK" && break
+done
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+	@echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
+echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE=EBTC
+ VERSION=0.1.0
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  STRIP=$ac_ct_STRIP
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+EBTC_LT_CURRENT=1
+EBTC_LT_REVISION=0
+EBTC_LT_AGE=1
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+	;;
+    conftest.$ac_ext )
+	# This is the source file.
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	# FIXME: I believe we export ac_cv_exeext for Libtool,
+	# but it would be cool to find out if it's true.  Does anybody
+	# maintain Libtool? --akim.
+	export ac_cv_exeext
+	break;;
+    * )
+	break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  export ac_cv_exeext
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX			-qlanglvl=ansi
+# Ultrix and OSF/1	-std1
+# HP-UX 10.20 and later	-Ae
+# HP-UX older versions	-Aa -D_HPUX_SOURCE
+# SVR4			-Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+          ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
+echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote="\""
+      _am_result=BSD
+   fi
+fi
+
+
+echo "$as_me:$LINENO: result: $_am_result" >&5
+echo "${ECHO_T}$_am_result" >&6
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then
+  enableval="$enable_dependency_tracking"
+
+fi;
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+
+
+if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+
+depcc="$CC"   am_compiler_list=
+
+echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+
+
+if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	    break 3
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking whether ln -s works" >&5
+echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+echo "${ECHO_T}no, using $LN_S" >&6
+fi
+
+
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+
+cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+ ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+
+
+
+
+echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
+echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6
+    # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+  enableval="$enable_maintainer_mode"
+  USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi;
+  echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
+echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6
+
+
+if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+# Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+  enableval="$enable_shared"
+  p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_shared=yes
+fi;
+
+# Check whether --enable-static or --disable-static was given.
+if test "${enable_static+set}" = set; then
+  enableval="$enable_static"
+  p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_static=yes
+fi;
+
+# Check whether --enable-fast-install or --disable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then
+  enableval="$enable_fast_install"
+  p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_fast_install=yes
+fi;
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6
+if test "${lt_cv_path_SED+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && break
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+SED=$lt_cv_path_SED
+
+fi
+
+echo "$as_me:$LINENO: result: $SED" >&5
+echo "${ECHO_T}$SED" >&6
+
+
+# Check whether --with-gnu-ld or --without-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+  withval="$with_gnu_ld"
+  test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi;
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6
+else
+  echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5
+echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6
+if test "${lt_cv_ld_reload_flag+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5
+echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+
+echo "$as_me:$LINENO: checking for BSD-compatible nm" >&5
+echo $ECHO_N "checking for BSD-compatible nm... $ECHO_C" >&6
+if test "${lt_cv_path_NM+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    tmp_nm="$ac_dir/${ac_tool_prefix}nm"
+    if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+      # Check to see if the nm accepts a BSD-compat flag.
+      # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+      #   nm: unknown option "B" ignored
+      # Tru64's nm complains that /dev/null is an invalid object file
+      case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+      */dev/null* | *'Invalid file or object type'*)
+	lt_cv_path_NM="$tmp_nm -B"
+	break
+        ;;
+      *)
+	case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	*/dev/null*)
+	  lt_cv_path_NM="$tmp_nm -p"
+	  break
+	  ;;
+	*)
+	  lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	  continue # so that we can try to find one that supports BSD flags
+	  ;;
+	esac
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+  test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm
+fi
+fi
+echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5
+echo "${ECHO_T}$lt_cv_path_NM" >&6
+NM="$lt_cv_path_NM"
+
+echo "$as_me:$LINENO: checking how to recognise dependent libraries" >&5
+echo $ECHO_N "checking how to recognise dependent libraries... $ECHO_C" >&6
+if test "${lt_cv_deplibs_check_method+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix4* | aix5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi4*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump'.
+  lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | kfreebsd*-gnu)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case "$host_cpu" in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+  if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+nto-qnx*)
+  lt_cv_deplibs_check_method=unknown
+  ;;
+
+openbsd*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB shared object'
+  else
+    lt_cv_deplibs_check_method='file_magic OpenBSD.* shared library'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sco3.2v5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5
+echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Check whether --enable-libtool-lock or --disable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+  enableval="$enable_libtool_lock"
+
+fi;
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *ELF-32*)
+      HPUX_IA64_MODE="32"
+      ;;
+    *ELF-64*)
+      HPUX_IA64_MODE="64"
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line 4396 "configure"' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+   if test "$lt_cv_prog_gnu_ld" = yes; then
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -melf32bsmip"
+      ;;
+    *N32*)
+      LD="${LD-ld} -melf32bmipn32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -melf64bmip"
+      ;;
+    esac
+   else
+    case `/usr/bin/file conftest.$ac_objext` in
+    *32-bit*)
+      LD="${LD-ld} -32"
+      ;;
+    *N32*)
+      LD="${LD-ld} -n32"
+      ;;
+    *64-bit*)
+      LD="${LD-ld} -64"
+      ;;
+    esac
+   fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    case "`/usr/bin/file conftest.o`" in
+    *32-bit*)
+      case $host in
+        x86_64-*linux*)
+          LD="${LD-ld} -m elf_i386"
+          ;;
+        ppc64-*linux*|powerpc64-*linux*)
+          LD="${LD-ld} -m elf32ppclinux"
+          ;;
+        s390x-*linux*)
+          LD="${LD-ld} -m elf_s390"
+          ;;
+        sparc64-*linux*)
+          LD="${LD-ld} -m elf32_sparc"
+          ;;
+      esac
+      ;;
+    *64-bit*)
+      case $host in
+        x86_64-*linux*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        ppc*-*linux*|powerpc*-*linux*)
+          LD="${LD-ld} -m elf64ppc"
+          ;;
+        s390*-*linux*)
+          LD="${LD-ld} -m elf64_s390"
+          ;;
+        sparc*-*linux*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5
+echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6
+if test "${lt_cv_cc_needs_belf+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  lt_cv_cc_needs_belf=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+lt_cv_cc_needs_belf=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5
+echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------- ##
+## Report this to libebtc@bugreport.1in1.de ##
+## ---------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  echo "$as_me:$LINENO: result: $CXX" >&5
+echo "${ECHO_T}$CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
+echo "${ECHO_T}$ac_ct_CXX" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CXX" && break
+done
+test -n "$ac_ct_CXX" || ac_ct_CXX="g++"
+
+  CXX=$ac_ct_CXX
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C++ compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6
+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6
+GXX=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+CXXFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cxx_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cxx_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cxx_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+depcc="$CXX"  am_compiler_list=
+
+echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
+if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CXX_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CXX_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CXX_dependencies_compiler_type=none
+fi
+
+fi
+echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5
+echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6
+CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
+
+
+
+if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
+  am__fastdepCXX_TRUE=
+  am__fastdepCXX_FALSE='#'
+else
+  am__fastdepCXX_TRUE='#'
+  am__fastdepCXX_FALSE=
+fi
+
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5
+echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6
+if test -z "$CXXCPP"; then
+  if test "${ac_cv_prog_CXXCPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CXXCPP needs to be expanded
+    for CXXCPP in "$CXX -E" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_cxx_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_cxx_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+  CXXCPP=$ac_cv_prog_CXXCPP
+else
+  ac_cv_prog_CXXCPP=$CXXCPP
+fi
+echo "$as_me:$LINENO: result: $CXXCPP" >&5
+echo "${ECHO_T}$CXXCPP" >&6
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_cxx_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_cxx_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+ac_ext=f
+ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5'
+ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_f77_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_F77+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$F77"; then
+  ac_cv_prog_F77="$F77" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_F77="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+F77=$ac_cv_prog_F77
+if test -n "$F77"; then
+  echo "$as_me:$LINENO: result: $F77" >&5
+echo "${ECHO_T}$F77" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$F77" && break
+  done
+fi
+if test -z "$F77"; then
+  ac_ct_F77=$F77
+  for ac_prog in g77 f77 xlf frt pgf77 fort77 fl32 af77 f90 xlf90 pgf90 epcf90 f95 fort xlf95 ifc efc pgf95 lf95 gfortran
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_F77+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_F77"; then
+  ac_cv_prog_ac_ct_F77="$ac_ct_F77" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_F77="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_F77=$ac_cv_prog_ac_ct_F77
+if test -n "$ac_ct_F77"; then
+  echo "$as_me:$LINENO: result: $ac_ct_F77" >&5
+echo "${ECHO_T}$ac_ct_F77" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_F77" && break
+done
+
+  F77=$ac_ct_F77
+fi
+
+
+# Provide some information about the compiler.
+echo "$as_me:5560:" \
+     "checking for Fortran 77 compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+rm -f a.out
+
+# If we don't use `.F' as extension, the preprocessor is not run on the
+# input file.  (Note that this only needs to work for GNU compilers.)
+ac_save_ext=$ac_ext
+ac_ext=F
+echo "$as_me:$LINENO: checking whether we are using the GNU Fortran 77 compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU Fortran 77 compiler... $ECHO_C" >&6
+if test "${ac_cv_f77_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+      program main
+#ifndef __GNUC__
+       choke me
+#endif
+
+      end
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_f77_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_f77_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_f77_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_f77_compiler_gnu" >&6
+ac_ext=$ac_save_ext
+ac_test_FFLAGS=${FFLAGS+set}
+ac_save_FFLAGS=$FFLAGS
+FFLAGS=
+echo "$as_me:$LINENO: checking whether $F77 accepts -g" >&5
+echo $ECHO_N "checking whether $F77 accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_f77_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  FFLAGS=-g
+cat >conftest.$ac_ext <<_ACEOF
+      program main
+
+      end
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_f77_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_f77_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_f77_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_f77_g" >&5
+echo "${ECHO_T}$ac_cv_prog_f77_g" >&6
+if test "$ac_test_FFLAGS" = set; then
+  FFLAGS=$ac_save_FFLAGS
+elif test $ac_cv_prog_f77_g = yes; then
+  if test "x$ac_cv_f77_compiler_gnu" = xyes; then
+    FFLAGS="-g -O2"
+  else
+    FFLAGS="-g"
+  fi
+else
+  if test "x$ac_cv_f77_compiler_gnu" = xyes; then
+    FFLAGS="-O2"
+  else
+    FFLAGS=
+  fi
+fi
+
+G77=`test $ac_compiler_gnu = yes && echo yes`
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers!
+
+# find the maximum length of command line arguments
+echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5
+echo $ECHO_N "checking the maximum length of command line arguments... $ECHO_C" >&6
+if test "${lt_cv_sys_max_cmd_len+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+    i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+ *)
+    # If test is not a shell built-in, we'll probably end up computing a
+    # maximum length that is only half of the actual maximum length, but
+    # we can't tell.
+    while (test "X"`$CONFIG_SHELL $0 --fallback-echo "X$teststring" 2>/dev/null` \
+	       = "XX$teststring") >/dev/null 2>&1 &&
+	    new_result=`expr "X$teststring" : ".*" 2>&1` &&
+	    lt_cv_sys_max_cmd_len=$new_result &&
+	    test $i != 17 # 1/2 MB should be enough
+    do
+      i=`expr $i + 1`
+      teststring=$teststring$teststring
+    done
+    teststring=
+    # Add a significant safety factor because C++ compilers can tack on massive
+    # amounts of additional arguments before passing them to the linker.
+    # It appears as though 1/2 is a usable value.
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    ;;
+  esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+  echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5
+echo "${ECHO_T}$lt_cv_sys_max_cmd_len" >&6
+else
+  echo "$as_me:$LINENO: result: none" >&5
+echo "${ECHO_T}none" >&6
+fi
+
+
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5
+echo $ECHO_N "checking command to parse $NM output from $compiler object... $ECHO_C" >&6
+if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Transform the above into a raw symbol and a C symbol.
+symxfrm='\1 \2\3 \3'
+
+# Transform an extracted symbol line into a proper C declaration
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([^ ]*\) \([^ ]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*) # Its linker distinguishes data from code symbols
+  if test "$host_cpu" = ia64; then
+    symcode='[ABCDEGRST]'
+  fi
+  lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+  lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/  {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (lt_ptr) \&\2},/p'"
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris* | sysv5*)
+  symcode='[BDRT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Try without a prefix undercore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Write the raw and C identifiers.
+  lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ 	]\($symcode$symcode*\)[ 	][ 	]*\($ac_symprfx\)$sympat$opt_cr$/$symxfrm/p'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+EOF
+
+  if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5
+  (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if grep ' nm_test_var$' "$nlist" >/dev/null; then
+	if grep ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext'
+
+	  cat <<EOF >> conftest.$ac_ext
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{
+EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext
+	  cat <<\EOF >> conftest.$ac_ext
+  {0, (lt_ptr_t) 0}
+};
+
+#ifdef __cplusplus
+}
+#endif
+EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_save_LIBS="$LIBS"
+	  lt_save_CFLAGS="$CFLAGS"
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+	  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS="$lt_save_LIBS"
+	  CFLAGS="$lt_save_CFLAGS"
+	else
+	  echo "cannot find nm_test_func in $nlist" >&5
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -f conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  echo "$as_me:$LINENO: result: failed" >&5
+echo "${ECHO_T}failed" >&6
+else
+  echo "$as_me:$LINENO: result: ok" >&5
+echo "${ECHO_T}ok" >&6
+fi
+
+echo "$as_me:$LINENO: checking for objdir" >&5
+echo $ECHO_N "checking for objdir... $ECHO_C" >&6
+if test "${lt_cv_objdir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5
+echo "${ECHO_T}$lt_cv_objdir" >&6
+objdir=$lt_cv_objdir
+
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e s/^X//'
+sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Constants:
+rm="rm -f"
+
+# Global variables:
+default_ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except M$VC,
+# which needs '.lib').
+libext=a
+ltmain="$ac_aux_dir/ltmain.sh"
+ofile="$default_ofile"
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AR+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="${ac_tool_prefix}ar"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+  ac_ct_AR=$AR
+  # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_AR+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="ar"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_AR" && ac_cv_prog_ac_ct_AR="false"
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  echo "$as_me:$LINENO: result: $ac_ct_AR" >&5
+echo "${ECHO_T}$ac_ct_AR" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  AR=$ac_ct_AR
+else
+  AR="$ac_cv_prog_AR"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  RANLIB=$ac_ct_RANLIB
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  STRIP=$ac_ct_STRIP
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+test -z "$AS" && AS=as
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+test -z "$LD" && LD=ld
+test -z "$LN_S" && LN_S="ln -s"
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+test -z "$NM" && NM=nm
+test -z "$SED" && SED=sed
+test -z "$OBJDUMP" && OBJDUMP=objdump
+test -z "$RANLIB" && RANLIB=:
+test -z "$STRIP" && STRIP=:
+test -z "$ac_objext" && ac_objext=o
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="\$RANLIB -t \$oldlib~$old_postinstall_cmds"
+    ;;
+  *)
+    old_postinstall_cmds="\$RANLIB \$oldlib~$old_postinstall_cmds"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+# Only perform the check for file, if the check method requires it
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5
+echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    echo "$as_me:$LINENO: checking for file" >&5
+echo $ECHO_N "checking for file... $ECHO_C" >&6
+if test "${lt_cv_path_MAGIC_CMD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex="`expr \"$deplibs_check_method\" : \"file_magic \(.*\)\"`"
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5
+echo "${ECHO_T}$MAGIC_CMD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+enable_dlopen=no
+enable_win32_dll=no
+
+# Check whether --enable-libtool-lock or --disable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then
+  enableval="$enable_libtool_lock"
+
+fi;
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+
+# Check whether --with-pic or --without-pic was given.
+if test "${with_pic+set}" = set; then
+  withval="$with_pic"
+  pic_mode="$withval"
+else
+  pic_mode=default
+fi;
+test -z "$pic_mode" && pic_mode=default
+
+# Use C for the default configuration in the libtool script
+tagname=
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}\n'
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+#
+# Check for any special shared library compilation flags.
+#
+lt_prog_cc_shlib=
+if test "$GCC" = no; then
+  case $host_os in
+  sco3.2v5*)
+    lt_prog_cc_shlib='-belf'
+    ;;
+  esac
+fi
+if test -n "$lt_prog_cc_shlib"; then
+  { echo "$as_me:$LINENO: WARNING: \`$CC' requires \`$lt_prog_cc_shlib' to build shared libraries" >&5
+echo "$as_me: WARNING: \`$CC' requires \`$lt_prog_cc_shlib' to build shared libraries" >&2;}
+  if echo "$old_CC $old_CFLAGS " | grep "[ 	]$lt_prog_cc_shlib[ 	]" >/dev/null; then :
+  else
+    { echo "$as_me:$LINENO: WARNING: add \`$lt_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&5
+echo "$as_me: WARNING: add \`$lt_prog_cc_shlib' to the CC or CFLAGS env variable and reconfigure" >&2;}
+    lt_cv_prog_cc_can_build_shared=no
+  fi
+fi
+
+
+#
+# Check to make sure the static flag actually works.
+#
+echo "$as_me:$LINENO: checking if $compiler static flag $lt_prog_compiler_static works" >&5
+echo $ECHO_N "checking if $compiler static flag $lt_prog_compiler_static works... $ECHO_C" >&6
+if test "${lt_prog_compiler_static_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_prog_compiler_static_works=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_prog_compiler_static"
+   printf "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+     else
+       lt_prog_compiler_static_works=yes
+     fi
+   fi
+   $rm conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works" >&5
+echo "${ECHO_T}$lt_prog_compiler_static_works" >&6
+
+if test x"$lt_prog_compiler_static_works" = xyes; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+  lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+
+echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:6592: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:6596: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+      ;;
+
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='-fPIC'
+	;;
+      esac
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      else
+	lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    linux*)
+      case $CC in
+      icc* | ecc*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-KPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      esac
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      lt_prog_compiler_pic='-Kpic'
+      lt_prog_compiler_static='-dn'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_prog_compiler_pic='-Kconform_pic'
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic" >&6
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+
+echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic works... $ECHO_C" >&6
+if test "${lt_prog_compiler_pic_works+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_prog_compiler_pic_works=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:6825: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:6829: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_prog_compiler_pic_works=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_works" >&6
+
+if test x"$lt_prog_compiler_pic_works" = xyes; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+case "$host_os" in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+
+echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $rm -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:6885: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:6889: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s out/conftest.err; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w .
+   $rm conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
+   $rm out/* && rmdir out
+   cd ..
+   rmdir conftest
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+  if test "$hard_links" = no; then
+    { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+  runpath_var=
+  allow_undefined_flag=
+  enable_shared_with_static_runtimes=no
+  archive_cmds=
+  archive_expsym_cmds=
+  old_archive_From_new_cmds=
+  old_archive_from_expsyms_cmds=
+  export_dynamic_flag_spec=
+  whole_archive_flag_spec=
+  thread_safe_flag_spec=
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_flag_spec_ld=
+  hardcode_libdir_separator=
+  hardcode_direct=no
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  link_all_deplibs=unknown
+  hardcode_automatic=no
+  module_cmds=
+  module_expsym_cmds=
+  always_export_symbols=no
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms="_GLOBAL_OFFSET_TABLE_"
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	ld_shlibs=no
+	cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+
+      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we can't use
+      # them.
+      ld_shlibs=no
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000  ${wl}--out-implib,$lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris* | sysv5*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+	ld_shlibs=no
+	cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+  linux*)
+    if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_cmds="$tmp_archive_cmds"
+      supports_anon_versioning=no
+      case `$LD -v 2>/dev/null` in
+        *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+        *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+        *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+        *\ 2.11.*) ;; # other 2.11 versions
+        *) supports_anon_versioning=yes ;;
+      esac
+      if test $supports_anon_versioning = yes; then
+        archive_expsym_cmds='$echo "{ global:" > $output_objdir/$libname.ver~
+cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+$echo "local: *; };" >> $output_objdir/$libname.ver~
+        $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+      else
+        archive_expsym_cmds="$tmp_archive_cmds"
+      fi
+      link_all_deplibs=no
+    else
+      ld_shlibs=no
+    fi
+    ;;
+
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs" = yes; then
+      runpath_var=LD_RUN_PATH
+      hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir'
+      export_dynamic_flag_spec='${wl}--export-dynamic'
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then
+ 	whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+  	whole_archive_flag_spec=
+      fi
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes && test -z "$link_static_flag"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix4* | aix5*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+	  export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+	  for ld_flag in $LDFLAGS; do
+  	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+  	    aix_use_runtimelinking=yes
+  	    break
+  	  fi
+	  done
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.012|aix4.012.*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" && \
+  	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	  then
+  	  # We have reworked collect2
+  	  hardcode_direct=yes
+	  else
+  	  # We have old collect2
+  	  hardcode_direct=unsupported
+  	  # It fails to find uninstalled libraries when the uninstalled
+  	  # path is not listed in the libpath.  Setting hardcode_minus_L
+  	  # to unsupported forces relinking
+  	  hardcode_minus_L=yes
+  	  hardcode_libdir_flag_spec='-L$libdir'
+  	  hardcode_libdir_separator=
+	  fi
+	esac
+	shared_flag='-shared'
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+  	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+  	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+  	if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+  	fi
+	fi
+      fi
+
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='-berok'
+       # Determine the default libpath from the value encoded in an empty executable.
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+       hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+	archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+       else
+	if test "$host_cpu" = ia64; then
+	  hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag="-z nodefs"
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an empty executable.
+	 cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+	 hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag=' ${wl}-bernotok'
+	  allow_undefined_flag=' ${wl}-berok'
+	  # -bexpall does not export symbols beginning with underscore (_)
+	  always_export_symbols=yes
+	  # Exported symbols can be pulled into shared objects from archives
+	  whole_archive_flag_spec=' '
+	  archive_cmds_need_lc=yes
+	  # This is similar to how AIX traditionally builds it's shared libraries.
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      # see comment about different semantics on the GNU ld section
+      ld_shlibs=no
+      ;;
+
+    bsdi4*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec=' '
+      allow_undefined_flag=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      old_archive_From_new_cmds='true'
+      # FIXME: Should let the user specify the lib program.
+      old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs'
+      fix_srcfile_path='`cygpath -w "$srcfile"`'
+      enable_shared_with_static_runtimes=yes
+      ;;
+
+    darwin* | rhapsody*)
+    if test "$GXX" = yes ; then
+      archive_cmds_need_lc=no
+      case "$host_os" in
+      rhapsody* | darwin1.[012])
+	allow_undefined_flag='-undefined suppress'
+	;;
+      *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	allow_undefined_flag='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[012])
+            allow_undefined_flag='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            allow_undefined_flag='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+	;;
+      esac
+    	lt_int_apple_cc_single_mod=no
+    	output_verbose_link_cmd='echo'
+    	if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+    	  lt_int_apple_cc_single_mod=yes
+    	fi
+    	if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+    	  archive_cmds='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    	else
+        archive_cmds='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+      fi
+      module_cmds='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+      # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+        if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+          archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        else
+          archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        fi
+          module_expsym_cmds='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+      hardcode_direct=no
+      hardcode_automatic=yes
+      hardcode_shlibpath_var=unsupported
+      whole_archive_flag_spec='-all_load $convenience'
+      link_all_deplibs=yes
+    else
+      ld_shlibs=no
+    fi
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    freebsd1*)
+      ld_shlibs=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | kfreebsd*-gnu)
+      archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	archive_cmds='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='${wl}-E'
+      ;;
+
+    hpux10* | hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	*)
+	  archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*)
+	  hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_flag_spec_ld='+b $libdir'
+	  hardcode_libdir_separator=:
+	  hardcode_direct=no
+	  hardcode_shlibpath_var=no
+	  ;;
+	ia64*)
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_direct=no
+	  hardcode_shlibpath_var=no
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L=yes
+	  ;;
+	*)
+	  hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_separator=:
+	  hardcode_direct=yes
+	  export_dynamic_flag_spec='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec_ld='-rpath $libdir'
+      fi
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      link_all_deplibs=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    openbsd*)
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	export_dynamic_flag_spec='${wl}-E'
+      else
+       case $host_os in
+	 openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	   archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	   hardcode_libdir_flag_spec='-R$libdir'
+	   ;;
+	 *)
+	   archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	   hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	   ;;
+       esac
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_From_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+	$LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      hardcode_libdir_separator=:
+      ;;
+
+    sco3.2v5*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='${wl}-Bexport'
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z text'
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	  $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp'
+      else
+	archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  	$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+	whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds='$CC -r -o $output$reload_objs'
+	  hardcode_direct=no
+        ;;
+	motorola)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4.2uw2*)
+      archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=no
+      hardcode_shlibpath_var=no
+      hardcode_runpath_var=yes
+      runpath_var=LD_RUN_PATH
+      ;;
+
+   sysv5OpenUNIX8* | sysv5UnixWare7* |  sysv5uw[78]* | unixware7*)
+      no_undefined_flag='${wl}-z ${wl}text'
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv5*)
+      no_undefined_flag=' -z text'
+      # $CC -shared without GNU ld will not create a library from C++
+      # object files and a static libstdc++, better avoid it by now
+      archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      hardcode_libdir_flag_spec=
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $ld_shlibs" >&5
+echo "${ECHO_T}$ld_shlibs" >&6
+test "$ld_shlibs" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+      $rm conftest*
+      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$lt_prog_compiler_wl
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$allow_undefined_flag
+        allow_undefined_flag=
+        if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+        then
+	  archive_cmds_need_lc=no
+        else
+	  archive_cmds_need_lc=yes
+        fi
+        allow_undefined_flag=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $rm conftest*
+      echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5
+echo "${ECHO_T}$archive_cmds_need_lc" >&6
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+if test "$GCC" = yes; then
+  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
+  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
+  if test "$GCC" = yes; then
+    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
+  else
+    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
+  fi
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+kfreebsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.01* | freebsdelf3.01*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  *) # from 3.2 on
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case "$host_cpu" in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+   hppa*64*)
+     shrext_cmds='.sl'
+     hardcode_into_libs=yes
+     dynamic_linker="$host_os dld.sl"
+     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+     soname_spec='${libname}${release}${shared_ext}$major'
+     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+     ;;
+   *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+knetbsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+nto-qnx*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=yes
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" || \
+   test -n "$runpath_var " || \
+   test "X$hardcode_automatic"="Xyes" ; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, )" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action" >&5
+echo "${ECHO_T}$hardcode_action" >&6
+
+if test "$hardcode_action" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+   darwin*)
+       if test -n "$STRIP" ; then
+         striplib="$STRIP -x"
+         echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+       else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+       ;;
+   *)
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+    ;;
+  esac
+fi
+
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+   ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+   ;;
+
+  *)
+    echo "$as_me:$LINENO: checking for shl_load" >&5
+echo $ECHO_N "checking for shl_load... $ECHO_C" >&6
+if test "${ac_cv_func_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+char (*f) () = shl_load;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shl_load;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+echo "${ECHO_T}$ac_cv_func_shl_load" >&6
+if test $ac_cv_func_shl_load = yes; then
+  lt_cv_dlopen="shl_load"
+else
+  echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+int
+main ()
+{
+shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6
+if test $ac_cv_lib_dld_shl_load = yes; then
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"
+else
+  echo "$as_me:$LINENO: checking for dlopen" >&5
+echo $ECHO_N "checking for dlopen... $ECHO_C" >&6
+if test "${ac_cv_func_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+char (*f) () = dlopen;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != dlopen;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+echo "${ECHO_T}$ac_cv_func_dlopen" >&6
+if test $ac_cv_func_dlopen = yes; then
+  lt_cv_dlopen="dlopen"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_svld_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_svld_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6
+if test $ac_cv_lib_svld_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dld_link ();
+int
+main ()
+{
+dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_dld_link=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_dld_link=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6
+if test $ac_cv_lib_dld_dld_link = yes; then
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 9073 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self" >&6
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 9171 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+# Report which librarie types wil actually be built
+echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $can_build_shared" >&5
+echo "${ECHO_T}$can_build_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+
+aix4* | aix5*)
+  if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+    test "$enable_shared" = yes && enable_static=no
+  fi
+  ;;
+  darwin* | rhapsody*)
+  if test "$GCC" = yes; then
+    archive_cmds_need_lc=no
+    case "$host_os" in
+    rhapsody* | darwin1.[012])
+      allow_undefined_flag='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	allow_undefined_flag='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[012])
+            allow_undefined_flag='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            allow_undefined_flag='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+      ;;
+    esac
+    output_verbose_link_cmd='echo'
+    archive_cmds='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring'
+    module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+    archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag  -o $lib $libobjs $deplibs$compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    module_expsym_cmds='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    hardcode_direct=no
+    hardcode_automatic=yes
+    hardcode_shlibpath_var=unsupported
+    whole_archive_flag_spec='-all_load $convenience'
+    link_all_deplibs=yes
+  else
+    ld_shlibs=no
+  fi
+    ;;
+esac
+echo "$as_me:$LINENO: result: $enable_shared" >&5
+echo "${ECHO_T}$enable_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+echo "$as_me:$LINENO: result: $enable_static" >&5
+echo "${ECHO_T}$enable_static" >&6
+
+# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    compiler \
+    CC \
+    LD \
+    lt_prog_compiler_wl \
+    lt_prog_compiler_pic \
+    lt_prog_compiler_static \
+    lt_prog_compiler_no_builtin_flag \
+    export_dynamic_flag_spec \
+    thread_safe_flag_spec \
+    whole_archive_flag_spec \
+    enable_shared_with_static_runtimes \
+    old_archive_cmds \
+    old_archive_from_new_cmds \
+    predep_objects \
+    postdep_objects \
+    predeps \
+    postdeps \
+    compiler_lib_search_path \
+    archive_cmds \
+    archive_expsym_cmds \
+    postinstall_cmds \
+    postuninstall_cmds \
+    old_archive_from_expsyms_cmds \
+    allow_undefined_flag \
+    no_undefined_flag \
+    export_symbols_cmds \
+    hardcode_libdir_flag_spec \
+    hardcode_libdir_flag_spec_ld \
+    hardcode_libdir_separator \
+    hardcode_automatic \
+    module_cmds \
+    module_expsym_cmds \
+    lt_cv_prog_compiler_c_o \
+    exclude_expsyms \
+    include_expsyms; do
+
+    case $var in
+    old_archive_cmds | \
+    old_archive_from_new_cmds | \
+    archive_cmds | \
+    archive_expsym_cmds | \
+    module_cmds | \
+    module_expsym_cmds | \
+    old_archive_from_expsyms_cmds | \
+    export_symbols_cmds | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\$0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+cfgfile="${ofile}T"
+  trap "$rm \"$cfgfile\"; exit 1" 1 2 15
+  $rm -f "$cfgfile"
+  { echo "$as_me:$LINENO: creating $ofile" >&5
+echo "$as_me: creating $ofile" >&6;}
+
+  cat <<__EOF__ >> "$cfgfile"
+#! $SHELL
+
+# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP)
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+#
+# This file is part of GNU Libtool:
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="$SED -e s/^X//"
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test "X\${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi
+
+# The names of the tagged configurations supported by this script.
+available_tags=
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_predep_objects
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_postdep_objects
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_predeps
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_postdeps
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$hardcode_automatic
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# ### END LIBTOOL CONFIG
+
+__EOF__
+
+
+  case $host_os in
+  aix3*)
+    cat <<\EOF >> "$cfgfile"
+
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+EOF
+    ;;
+  esac
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1)
+
+  mv -f "$cfgfile" "$ofile" || \
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+# Check whether --with-tags or --without-tags was given.
+if test "${with_tags+set}" = set; then
+  withval="$with_tags"
+  tagnames="$withval"
+fi;
+
+if test -f "$ltmain" && test -n "$tagnames"; then
+  if test ! -f "${ofile}"; then
+    { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not exist" >&5
+echo "$as_me: WARNING: output file \`$ofile' does not exist" >&2;}
+  fi
+
+  if test -z "$LTCC"; then
+    eval "`$SHELL ${ofile} --config | grep '^LTCC='`"
+    if test -z "$LTCC"; then
+      { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not look like a libtool script" >&5
+echo "$as_me: WARNING: output file \`$ofile' does not look like a libtool script" >&2;}
+    else
+      { echo "$as_me:$LINENO: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&5
+echo "$as_me: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&2;}
+    fi
+  fi
+
+  # Extract list of available tagged configurations in $ofile.
+  # Note that this assumes the entire list is on one line.
+  available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'`
+
+  lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+  for tagname in $tagnames; do
+    IFS="$lt_save_ifs"
+    # Check whether tagname contains only valid characters
+    case `$echo "X$tagname" | $Xsed -e 's:[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]::g'` in
+    "") ;;
+    *)  { { echo "$as_me:$LINENO: error: invalid tag name: $tagname" >&5
+echo "$as_me: error: invalid tag name: $tagname" >&2;}
+   { (exit 1); exit 1; }; }
+	;;
+    esac
+
+    if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null
+    then
+      { { echo "$as_me:$LINENO: error: tag name \"$tagname\" already exists" >&5
+echo "$as_me: error: tag name \"$tagname\" already exists" >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+
+    # Update the list of available tags.
+    if test -n "$tagname"; then
+      echo appending configuration tag \"$tagname\" to $ofile
+
+      case $tagname in
+      CXX)
+	if test -n "$CXX" && test "X$CXX" != "Xno"; then
+	  ac_ext=cc
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+
+archive_cmds_need_lc_CXX=no
+allow_undefined_flag_CXX=
+always_export_symbols_CXX=no
+archive_expsym_cmds_CXX=
+export_dynamic_flag_spec_CXX=
+hardcode_direct_CXX=no
+hardcode_libdir_flag_spec_CXX=
+hardcode_libdir_flag_spec_ld_CXX=
+hardcode_libdir_separator_CXX=
+hardcode_minus_L_CXX=no
+hardcode_automatic_CXX=no
+module_cmds_CXX=
+module_expsym_cmds_CXX=
+link_all_deplibs_CXX=unknown
+old_archive_cmds_CXX=$old_archive_cmds
+no_undefined_flag_CXX=
+whole_archive_flag_spec_CXX=
+enable_shared_with_static_runtimes_CXX=no
+
+# Dependencies to place before and after the object being linked:
+predep_objects_CXX=
+postdep_objects_CXX=
+predeps_CXX=
+postdeps_CXX=
+compiler_lib_search_path_CXX=
+
+# Source file extension for C++ test sources.
+ac_ext=cc
+
+# Object file extension for compiled C++ test sources.
+objext=o
+objext_CXX=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(int, char *) { return(0); }\n'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_LD=$LD
+lt_save_GCC=$GCC
+GCC=$GXX
+lt_save_with_gnu_ld=$with_gnu_ld
+lt_save_path_LD=$lt_cv_path_LD
+if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+  lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+else
+  unset lt_cv_prog_gnu_ld
+fi
+if test -n "${lt_cv_path_LDCXX+set}"; then
+  lt_cv_path_LD=$lt_cv_path_LDCXX
+else
+  unset lt_cv_path_LD
+fi
+test -z "${LDCXX+set}" || LD=$LDCXX
+CC=${CXX-"c++"}
+compiler=$CC
+compiler_CXX=$CC
+cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
+
+# We don't want -fno-exception wen compiling C++ code, so set the
+# no_builtin_flag separately
+if test "$GXX" = yes; then
+  lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
+else
+  lt_prog_compiler_no_builtin_flag_CXX=
+fi
+
+if test "$GXX" = yes; then
+  # Set up default GNU C++ configuration
+
+
+# Check whether --with-gnu-ld or --without-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then
+  withval="$with_gnu_ld"
+  test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi;
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  echo "$as_me:$LINENO: checking for ld used by $CC" >&5
+echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  echo "$as_me:$LINENO: checking for GNU ld" >&5
+echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6
+else
+  echo "$as_me:$LINENO: checking for non-GNU ld" >&5
+echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6
+fi
+if test "${lt_cv_path_LD+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  echo "$as_me:$LINENO: result: $LD" >&5
+echo "${ECHO_T}$LD" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5
+echo "$as_me: error: no acceptable ld found in \$PATH" >&2;}
+   { (exit 1); exit 1; }; }
+echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5
+echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6
+if test "${lt_cv_prog_gnu_ld+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_gnu_ld" >&5
+echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+  # Check if GNU C++ uses GNU ld as the underlying linker, since the
+  # archiving commands below assume that GNU ld is being used.
+  if test "$with_gnu_ld" = yes; then
+    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+    archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+    hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir'
+    export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+    #     investigate it a little bit more. (MM)
+    wlarc='${wl}'
+
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if eval "`$CC -print-prog-name=ld` --help 2>&1" | \
+	grep 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      whole_archive_flag_spec_CXX=
+    fi
+  else
+    with_gnu_ld=no
+    wlarc=
+
+    # A generic and very simple default shared library creation
+    # command for GNU C++ for the case where it uses the native
+    # linker, instead of GNU ld.  If possible, this setting should
+    # overridden to take advantage of the native linker features on
+    # the platform it is being used on.
+    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+  fi
+
+  # Commands to make compiler produce verbose output that lists
+  # what "hidden" libraries, object files and flags are used when
+  # linking a shared library.
+  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+else
+  GXX=no
+  with_gnu_ld=no
+  wlarc=
+fi
+
+# PORTME: fill in a description of your system's C++ link characteristics
+echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+ld_shlibs_CXX=yes
+case $host_os in
+  aix3*)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+  aix4* | aix5*)
+    if test "$host_cpu" = ia64; then
+      # On IA64, the linker does run time linking by default, so we don't
+      # have to do anything special.
+      aix_use_runtimelinking=no
+      exp_sym_flag='-Bexport'
+      no_entry_flag=""
+    else
+      aix_use_runtimelinking=no
+
+      # Test if we are trying to use run time linking or normal
+      # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+      # need to do runtime linking.
+      case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+	for ld_flag in $LDFLAGS; do
+	  case $ld_flag in
+	  *-brtl*)
+	    aix_use_runtimelinking=yes
+	    break
+	    ;;
+	  esac
+	done
+      esac
+
+      exp_sym_flag='-bexport'
+      no_entry_flag='-bnoentry'
+    fi
+
+    # When large executables or shared objects are built, AIX ld can
+    # have problems creating the table of contents.  If linking a library
+    # or program results in "error TOC overflow" add -mminimal-toc to
+    # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+    # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+    archive_cmds_CXX=''
+    hardcode_direct_CXX=yes
+    hardcode_libdir_separator_CXX=':'
+    link_all_deplibs_CXX=yes
+
+    if test "$GXX" = yes; then
+      case $host_os in aix4.012|aix4.012.*)
+      # We only want to do this on AIX 4.2 and lower, the check
+      # below for broken collect2 doesn't work under 4.3+
+	collect2name=`${CC} -print-prog-name=collect2`
+	if test -f "$collect2name" && \
+	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	then
+	  # We have reworked collect2
+	  hardcode_direct_CXX=yes
+	else
+	  # We have old collect2
+	  hardcode_direct_CXX=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L_CXX=yes
+	  hardcode_libdir_flag_spec_CXX='-L$libdir'
+	  hardcode_libdir_separator_CXX=
+	fi
+      esac
+      shared_flag='-shared'
+    else
+      # not using gcc
+      if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	shared_flag='-G'
+      else
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag='${wl}-G'
+	else
+	  shared_flag='${wl}-bM:SRE'
+	fi
+      fi
+    fi
+
+    # It seems that -bexpall does not export symbols beginning with
+    # underscore (_), so it is better to generate a list of symbols to export.
+    always_export_symbols_CXX=yes
+    if test "$aix_use_runtimelinking" = yes; then
+      # Warning - without using the other runtime loading flags (-brtl),
+      # -berok will link without error, but may produce a broken library.
+      allow_undefined_flag_CXX='-berok'
+      # Determine the default libpath from the value encoded in an empty executable.
+      cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+      hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+      archive_expsym_cmds_CXX="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+     else
+      if test "$host_cpu" = ia64; then
+	hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib'
+	allow_undefined_flag_CXX="-z nodefs"
+	archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+      else
+	# Determine the default libpath from the value encoded in an empty executable.
+	cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+	hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath"
+	# Warning - without using the other run time loading flags,
+	# -berok will link without error, but may produce a broken library.
+	no_undefined_flag_CXX=' ${wl}-bernotok'
+	allow_undefined_flag_CXX=' ${wl}-berok'
+	# -bexpall does not export symbols beginning with underscore (_)
+	always_export_symbols_CXX=yes
+	# Exported symbols can be pulled into shared objects from archives
+	whole_archive_flag_spec_CXX=' '
+	archive_cmds_need_lc_CXX=yes
+	# This is similar to how AIX traditionally builds it's shared libraries.
+	archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+      fi
+    fi
+    ;;
+  chorus*)
+    case $cc_basename in
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+
+  cygwin* | mingw* | pw32*)
+    # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless,
+    # as there is no search path for DLLs.
+    hardcode_libdir_flag_spec_CXX='-L$libdir'
+    allow_undefined_flag_CXX=unsupported
+    always_export_symbols_CXX=no
+    enable_shared_with_static_runtimes_CXX=yes
+
+    if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+      archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+      # If the export-symbols file already is a .def file (1st line
+      # is EXPORTS), use it as is; otherwise, prepend...
+      archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	cp $export_symbols $output_objdir/$soname.def;
+      else
+	echo EXPORTS > $output_objdir/$soname.def;
+	cat $export_symbols >> $output_objdir/$soname.def;
+      fi~
+      $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+    else
+      ld_shlibs_CXX=no
+    fi
+  ;;
+
+  darwin* | rhapsody*)
+  if test "$GXX" = yes; then
+    archive_cmds_need_lc_CXX=no
+    case "$host_os" in
+    rhapsody* | darwin1.[012])
+      allow_undefined_flag_CXX='-undefined suppress'
+      ;;
+    *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	allow_undefined_flag_CXX='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[012])
+            allow_undefined_flag_CXX='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            allow_undefined_flag_CXX='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+      ;;
+    esac
+    lt_int_apple_cc_single_mod=no
+    output_verbose_link_cmd='echo'
+    if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+      lt_int_apple_cc_single_mod=yes
+    fi
+    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+      archive_cmds_CXX='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    else
+      archive_cmds_CXX='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    fi
+    module_cmds_CXX='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+
+    # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+    if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+      archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    else
+      archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    hardcode_direct_CXX=no
+    hardcode_automatic_CXX=yes
+    hardcode_shlibpath_var_CXX=unsupported
+    whole_archive_flag_spec_CXX='-all_load $convenience'
+    link_all_deplibs_CXX=yes
+  else
+    ld_shlibs_CXX=no
+  fi
+    ;;
+
+  dgux*)
+    case $cc_basename in
+      ec++)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      ghcx)
+	# Green Hills C++ Compiler
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+  freebsd12*)
+    # C++ shared libraries reported to be fairly broken before switch to ELF
+    ld_shlibs_CXX=no
+    ;;
+  freebsd-elf*)
+    archive_cmds_need_lc_CXX=no
+    ;;
+  freebsd* | kfreebsd*-gnu)
+    # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+    # conventions
+    ld_shlibs_CXX=yes
+    ;;
+  gnu*)
+    ;;
+  hpux9*)
+    hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+    hardcode_libdir_separator_CXX=:
+    export_dynamic_flag_spec_CXX='${wl}-E'
+    hardcode_direct_CXX=yes
+    hardcode_minus_L_CXX=yes # Not in the search PATH,
+				# but as the default
+				# location of the library.
+
+    case $cc_basename in
+    CC)
+      # FIXME: insert proper C++ library support
+      ld_shlibs_CXX=no
+      ;;
+    aCC)
+      archive_cmds_CXX='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      #
+      # There doesn't appear to be a way to prevent this compiler from
+      # explicitly linking system object files so we need to strip them
+      # from the output so that they don't get included in the library
+      # dependencies.
+      output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+      ;;
+    *)
+      if test "$GXX" = yes; then
+        archive_cmds_CXX='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+        # FIXME: insert proper C++ library support
+        ld_shlibs_CXX=no
+      fi
+      ;;
+    esac
+    ;;
+  hpux10*|hpux11*)
+    if test $with_gnu_ld = no; then
+      case "$host_cpu" in
+      hppa*64*)
+	hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+	hardcode_libdir_flag_spec_ld_CXX='+b $libdir'
+	hardcode_libdir_separator_CXX=:
+        ;;
+      ia64*)
+	hardcode_libdir_flag_spec_CXX='-L$libdir'
+        ;;
+      *)
+	hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir'
+	hardcode_libdir_separator_CXX=:
+	export_dynamic_flag_spec_CXX='${wl}-E'
+        ;;
+      esac
+    fi
+    case "$host_cpu" in
+    hppa*64*)
+      hardcode_direct_CXX=no
+      hardcode_shlibpath_var_CXX=no
+      ;;
+    ia64*)
+      hardcode_direct_CXX=no
+      hardcode_shlibpath_var_CXX=no
+      hardcode_minus_L_CXX=yes # Not in the search PATH,
+					      # but as the default
+					      # location of the library.
+      ;;
+    *)
+      hardcode_direct_CXX=yes
+      hardcode_minus_L_CXX=yes # Not in the search PATH,
+					      # but as the default
+					      # location of the library.
+      ;;
+    esac
+
+    case $cc_basename in
+      CC)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      aCC)
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds_CXX='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
+	  ;;
+	*)
+	  archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	  ;;
+	esac
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes; then
+	  if test $with_gnu_ld = no; then
+	    case "$host_cpu" in
+	    ia64*|hppa*64*)
+	      archive_cmds_CXX='$LD -b +h $soname -o $lib $linker_flags $libobjs $deplibs'
+	      ;;
+	    *)
+	      archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	      ;;
+	    esac
+	  fi
+	else
+	  # FIXME: insert proper C++ library support
+	  ld_shlibs_CXX=no
+	fi
+	;;
+    esac
+    ;;
+  irix5* | irix6*)
+    case $cc_basename in
+      CC)
+	# SGI C++
+	archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+
+	# Archives containing C++ object files must be created using
+	# "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+	# necessary to make sure instantiated templates are included
+	# in the archive.
+	old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs'
+	;;
+      *)
+	if test "$GXX" = yes; then
+	  if test "$with_gnu_ld" = no; then
+	    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+	  else
+	    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib'
+	  fi
+	fi
+	link_all_deplibs_CXX=yes
+	;;
+    esac
+    hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+    hardcode_libdir_separator_CXX=:
+    ;;
+  linux*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+	archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+
+	hardcode_libdir_flag_spec_CXX='${wl}--rpath,$libdir'
+	export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+
+	# Archives containing C++ object files must be created using
+	# "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'
+	;;
+      icpc)
+	# Intel C++
+	with_gnu_ld=yes
+	archive_cmds_need_lc_CXX=no
+	archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+	whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	;;
+      cxx)
+	# Compaq C++
+	archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+	runpath_var=LD_RUN_PATH
+	hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+	hardcode_libdir_separator_CXX=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+    esac
+    ;;
+  lynxos*)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+  m88k*)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+  mvs*)
+    case $cc_basename in
+      cxx)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+  netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+    if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+      archive_cmds_CXX='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+      wlarc=
+      hardcode_libdir_flag_spec_CXX='-R$libdir'
+      hardcode_direct_CXX=yes
+      hardcode_shlibpath_var_CXX=no
+    fi
+    # Workaround some broken pre-1.5 toolchains
+    output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+    ;;
+  osf3*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	hardcode_libdir_separator_CXX=:
+
+	# Archives containing C++ object files must be created using
+	# "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'
+
+	;;
+      RCC)
+	# Rational C++ 2.4.1
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      cxx)
+	allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+
+	hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+	hardcode_libdir_separator_CXX=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+	  archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+
+	  hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+	  hardcode_libdir_separator_CXX=:
+
+	  # Commands to make compiler produce verbose output that lists
+	  # what "hidden" libraries, object files and flags are used when
+	  # linking a shared library.
+	  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+	else
+	  # FIXME: insert proper C++ library support
+	  ld_shlibs_CXX=no
+	fi
+	;;
+    esac
+    ;;
+  osf4* | osf5*)
+    case $cc_basename in
+      KCC)
+	# Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	# KCC will only create a shared library if the output file
+	# ends with ".so" (or ".sl" for HP-UX), so rename the library
+	# to its proper name (with version) after linking.
+	archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir'
+	hardcode_libdir_separator_CXX=:
+
+	# Archives containing C++ object files must be created using
+	# the KAI C++ compiler.
+	old_archive_cmds_CXX='$CC -o $oldlib $oldobjs'
+	;;
+      RCC)
+	# Rational C++ 2.4.1
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      cxx)
+	allow_undefined_flag_CXX=' -expect_unresolved \*'
+	archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib'
+	archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+	  echo "-hidden">> $lib.exp~
+	  $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp  `test -n "$verstring" && echo -set_version	$verstring` -update_registry $objdir/so_locations -o $lib~
+	  $rm $lib.exp'
+
+	hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+	hardcode_libdir_separator_CXX=:
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+	;;
+      *)
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*'
+	 archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${objdir}/so_locations -o $lib'
+
+	  hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+	  hardcode_libdir_separator_CXX=:
+
+	  # Commands to make compiler produce verbose output that lists
+	  # what "hidden" libraries, object files and flags are used when
+	  # linking a shared library.
+	  output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"'
+
+	else
+	  # FIXME: insert proper C++ library support
+	  ld_shlibs_CXX=no
+	fi
+	;;
+    esac
+    ;;
+  psos*)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+  sco*)
+    archive_cmds_need_lc_CXX=no
+    case $cc_basename in
+      CC)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+  sunos4*)
+    case $cc_basename in
+      CC)
+	# Sun C++ 4.x
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      lcc)
+	# Lucid
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+  solaris*)
+    case $cc_basename in
+      CC)
+	# Sun C++ 4.2, 5.x and Centerline C++
+	no_undefined_flag_CXX=' -zdefs'
+	archive_cmds_CXX='$CC -G${allow_undefined_flag} -nolib -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	$CC -G${allow_undefined_flag} -nolib ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	hardcode_libdir_flag_spec_CXX='-R$libdir'
+	hardcode_shlibpath_var_CXX=no
+	case $host_os in
+	  solaris2.0-5 | solaris2.0-5.*) ;;
+	  *)
+	    # The C++ compiler is used as linker so we must use $wl
+	    # flag to pass the commands to the underlying system
+	    # linker.
+	    # Supported since Solaris 2.6 (maybe 2.5.1?)
+	    whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	    ;;
+	esac
+	link_all_deplibs_CXX=yes
+
+	# Commands to make compiler produce verbose output that lists
+	# what "hidden" libraries, object files and flags are used when
+	# linking a shared library.
+	#
+	# There doesn't appear to be a way to prevent this compiler from
+	# explicitly linking system object files so we need to strip them
+	# from the output so that they don't get included in the library
+	# dependencies.
+	output_verbose_link_cmd='templist=`$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep "\-[LR]"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list'
+
+	# Archives containing C++ object files must be created using
+	# "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	# necessary to make sure instantiated templates are included
+	# in the archive.
+	old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
+	;;
+      gcx)
+	# Green Hills C++ Compiler
+	archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+	# The C++ compiler must be used to create the archive.
+	old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+	;;
+      *)
+	# GNU C++ compiler with Solaris linker
+	if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	  no_undefined_flag_CXX=' ${wl}-z ${wl}defs'
+	  if $CC --version | grep -v '^2\.7' > /dev/null; then
+	    archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	    archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
+	  else
+	    # g++ 2.7 appears to require `-G' NOT `-shared' on this
+	    # platform.
+	    archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	    archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+		$CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp'
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\""
+	  fi
+
+	  hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir'
+	fi
+	;;
+    esac
+    ;;
+  sysv5OpenUNIX8* | sysv5UnixWare7* | sysv5uw[78]* | unixware7*)
+    archive_cmds_need_lc_CXX=no
+    ;;
+  tandem*)
+    case $cc_basename in
+      NCC)
+	# NonStop-UX NCC 3.20
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+      *)
+	# FIXME: insert proper C++ library support
+	ld_shlibs_CXX=no
+	;;
+    esac
+    ;;
+  vxworks*)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+  *)
+    # FIXME: insert proper C++ library support
+    ld_shlibs_CXX=no
+    ;;
+esac
+echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5
+echo "${ECHO_T}$ld_shlibs_CXX" >&6
+test "$ld_shlibs_CXX" = no && can_build_shared=no
+
+GCC_CXX="$GXX"
+LD_CXX="$LD"
+
+
+cat > conftest.$ac_ext <<EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+EOF
+
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  # The `*' in the case matches for architectures that use `case' in
+  # $output_verbose_cmd can trigger glob expansion during the loop
+  # eval without this substitution.
+  output_verbose_link_cmd="`$echo \"X$output_verbose_link_cmd\" | $Xsed -e \"$no_glob_subst\"`"
+
+  for p in `eval $output_verbose_link_cmd`; do
+    case $p in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" \
+	  || test $p = "-R"; then
+	 prev=$p
+	 continue
+       else
+	 prev=
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 case $p in
+	 -L* | -R*)
+	   # Internal compiler library paths should come after those
+	   # provided the user.  The postdeps already come after the
+	   # user supplied libs so there is no need to process them.
+	   if test -z "$compiler_lib_search_path_CXX"; then
+	     compiler_lib_search_path_CXX="${prev}${p}"
+	   else
+	     compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}"
+	   fi
+	   ;;
+	 # The "-l" case would never come before the object being
+	 # linked, so don't bother handling this case.
+	 esac
+       else
+	 if test -z "$postdeps_CXX"; then
+	   postdeps_CXX="${prev}${p}"
+	 else
+	   postdeps_CXX="${postdeps_CXX} ${prev}${p}"
+	 fi
+       fi
+       ;;
+
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+	 pre_test_object_deps_done=yes
+	 continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 if test -z "$predep_objects_CXX"; then
+	   predep_objects_CXX="$p"
+	 else
+	   predep_objects_CXX="$predep_objects_CXX $p"
+	 fi
+       else
+	 if test -z "$postdep_objects_CXX"; then
+	   postdep_objects_CXX="$p"
+	 else
+	   postdep_objects_CXX="$postdep_objects_CXX $p"
+	 fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling CXX test program"
+fi
+
+$rm -f confest.$objext
+
+case " $postdeps_CXX " in
+*" -lc "*) archive_cmds_need_lc_CXX=no ;;
+esac
+
+lt_prog_compiler_wl_CXX=
+lt_prog_compiler_pic_CXX=
+lt_prog_compiler_static_CXX=
+
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    lt_prog_compiler_wl_CXX='-Wl,'
+    lt_prog_compiler_static_CXX='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_CXX='-Bstatic'
+      fi
+      ;;
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4'
+      ;;
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | os2* | pw32*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic_CXX='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      lt_prog_compiler_pic_CXX=
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic_CXX=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	;;
+      *)
+	lt_prog_compiler_pic_CXX='-fPIC'
+	;;
+      esac
+      ;;
+    *)
+      lt_prog_compiler_pic_CXX='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix4* | aix5*)
+	# All AIX code is PIC.
+	if test "$host_cpu" = ia64; then
+	  # AIX 5 now supports IA64 processor
+	  lt_prog_compiler_static_CXX='-Bstatic'
+	else
+	  lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp'
+	fi
+	;;
+      chorus*)
+	case $cc_basename in
+	cxch68)
+	  # Green Hills C++ Compiler
+	  # _LT_AC_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+	  ;;
+	esac
+	;;
+      dgux*)
+	case $cc_basename in
+	  ec++)
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    ;;
+	  ghcx)
+	    # Green Hills C++ Compiler
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      freebsd* | kfreebsd*-gnu)
+	# FreeBSD uses GNU C++
+	;;
+      hpux9* | hpux10* | hpux11*)
+	case $cc_basename in
+	  CC)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
+	    if test "$host_cpu" != ia64; then
+	      lt_prog_compiler_pic_CXX='+Z'
+	    fi
+	    ;;
+	  aCC)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX="${ac_cv_prog_cc_wl}-a ${ac_cv_prog_cc_wl}archive"
+	    case "$host_cpu" in
+	    hppa*64*|ia64*)
+	      # +Z the default
+	      ;;
+	    *)
+	      lt_prog_compiler_pic_CXX='+Z'
+	      ;;
+	    esac
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      irix5* | irix6* | nonstopux*)
+	case $cc_basename in
+	  CC)
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    # CC pic flag -KPIC is the default.
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      linux*)
+	case $cc_basename in
+	  KCC)
+	    # KAI C++ Compiler
+	    lt_prog_compiler_wl_CXX='--backend -Wl,'
+	    lt_prog_compiler_pic_CXX='-fPIC'
+	    ;;
+	  icpc)
+	    # Intel C++
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    lt_prog_compiler_static_CXX='-static'
+	    ;;
+	  cxx)
+	    # Compaq C++
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    lt_prog_compiler_pic_CXX=
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      lynxos*)
+	;;
+      m88k*)
+	;;
+      mvs*)
+	case $cc_basename in
+	  cxx)
+	    lt_prog_compiler_pic_CXX='-W c,exportall'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+	;;
+      osf3* | osf4* | osf5*)
+	case $cc_basename in
+	  KCC)
+	    lt_prog_compiler_wl_CXX='--backend -Wl,'
+	    ;;
+	  RCC)
+	    # Rational C++ 2.4.1
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  cxx)
+	    # Digital/Compaq C++
+	    lt_prog_compiler_wl_CXX='-Wl,'
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    lt_prog_compiler_pic_CXX=
+	    lt_prog_compiler_static_CXX='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      psos*)
+	;;
+      sco*)
+	case $cc_basename in
+	  CC)
+	    lt_prog_compiler_pic_CXX='-fPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      solaris*)
+	case $cc_basename in
+	  CC)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    lt_prog_compiler_wl_CXX='-Qoption ld '
+	    ;;
+	  gcx)
+	    # Green Hills C++ Compiler
+	    lt_prog_compiler_pic_CXX='-PIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sunos4*)
+	case $cc_basename in
+	  CC)
+	    # Sun C++ 4.x
+	    lt_prog_compiler_pic_CXX='-pic'
+	    lt_prog_compiler_static_CXX='-Bstatic'
+	    ;;
+	  lcc)
+	    # Lucid
+	    lt_prog_compiler_pic_CXX='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      tandem*)
+	case $cc_basename in
+	  NCC)
+	    # NonStop-UX NCC 3.20
+	    lt_prog_compiler_pic_CXX='-KPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      unixware*)
+	;;
+      vxworks*)
+	;;
+      *)
+	lt_prog_compiler_can_build_shared_CXX=no
+	;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_CXX" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_CXX" >&6
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic_CXX"; then
+
+echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... $ECHO_C" >&6
+if test "${lt_prog_compiler_pic_works_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_prog_compiler_pic_works_CXX=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:11348: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:11352: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_prog_compiler_pic_works_CXX=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_CXX" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_works_CXX" >&6
+
+if test x"$lt_prog_compiler_pic_works_CXX" = xyes; then
+    case $lt_prog_compiler_pic_CXX in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;;
+     esac
+else
+    lt_prog_compiler_pic_CXX=
+     lt_prog_compiler_can_build_shared_CXX=no
+fi
+
+fi
+case "$host_os" in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic_CXX=
+    ;;
+  *)
+    lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC"
+    ;;
+esac
+
+echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_c_o_CXX=no
+   $rm -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:11408: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:11412: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s out/conftest.err; then
+       lt_cv_prog_compiler_c_o_CXX=yes
+     fi
+   fi
+   chmod u+w .
+   $rm conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
+   $rm out/* && rmdir out
+   cd ..
+   rmdir conftest
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_CXX" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o_CXX" >&6
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+  if test "$hard_links" = no; then
+    { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+  export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  case $host_os in
+  aix4* | aix5*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+      export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+    else
+      export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    export_symbols_cmds_CXX="$ltdll_cmds"
+  ;;
+  cygwin* | mingw*)
+    export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  linux*)
+    link_all_deplibs_CXX=no
+  ;;
+  *)
+    export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  ;;
+  esac
+
+echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5
+echo "${ECHO_T}$ld_shlibs_CXX" >&6
+test "$ld_shlibs_CXX" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc_CXX" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc_CXX=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds_CXX in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+      $rm conftest*
+      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$lt_prog_compiler_wl_CXX
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$allow_undefined_flag_CXX
+        allow_undefined_flag_CXX=
+        if { (eval echo "$as_me:$LINENO: \"$archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+        then
+	  archive_cmds_need_lc_CXX=no
+        else
+	  archive_cmds_need_lc_CXX=yes
+        fi
+        allow_undefined_flag_CXX=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $rm conftest*
+      echo "$as_me:$LINENO: result: $archive_cmds_need_lc_CXX" >&5
+echo "${ECHO_T}$archive_cmds_need_lc_CXX" >&6
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+if test "$GCC" = yes; then
+  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
+  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
+  if test "$GCC" = yes; then
+    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
+  else
+    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
+  fi
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+kfreebsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.01* | freebsdelf3.01*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  *) # from 3.2 on
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case "$host_cpu" in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+   hppa*64*)
+     shrext_cmds='.sl'
+     hardcode_into_libs=yes
+     dynamic_linker="$host_os dld.sl"
+     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+     soname_spec='${libname}${release}${shared_ext}$major'
+     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+     ;;
+   *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+knetbsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+nto-qnx*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=yes
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action_CXX=
+if test -n "$hardcode_libdir_flag_spec_CXX" || \
+   test -n "$runpath_var CXX" || \
+   test "X$hardcode_automatic_CXX"="Xyes" ; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct_CXX" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, CXX)" != no &&
+     test "$hardcode_minus_L_CXX" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action_CXX=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action_CXX=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action_CXX=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action_CXX" >&5
+echo "${ECHO_T}$hardcode_action_CXX" >&6
+
+if test "$hardcode_action_CXX" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+   darwin*)
+       if test -n "$STRIP" ; then
+         striplib="$STRIP -x"
+         echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+       else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+       ;;
+   *)
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+    ;;
+  esac
+fi
+
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+   ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+   ;;
+
+  *)
+    echo "$as_me:$LINENO: checking for shl_load" >&5
+echo $ECHO_N "checking for shl_load... $ECHO_C" >&6
+if test "${ac_cv_func_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+char (*f) () = shl_load;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shl_load;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+echo "${ECHO_T}$ac_cv_func_shl_load" >&6
+if test $ac_cv_func_shl_load = yes; then
+  lt_cv_dlopen="shl_load"
+else
+  echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+int
+main ()
+{
+shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6
+if test $ac_cv_lib_dld_shl_load = yes; then
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"
+else
+  echo "$as_me:$LINENO: checking for dlopen" >&5
+echo $ECHO_N "checking for dlopen... $ECHO_C" >&6
+if test "${ac_cv_func_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+char (*f) () = dlopen;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != dlopen;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+echo "${ECHO_T}$ac_cv_func_dlopen" >&6
+if test $ac_cv_func_dlopen = yes; then
+  lt_cv_dlopen="dlopen"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_svld_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_svld_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6
+if test $ac_cv_lib_svld_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dld_link ();
+int
+main ()
+{
+dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_cxx_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_dld_link=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_dld_link=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6
+if test $ac_cv_lib_dld_dld_link = yes; then
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 12777 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self" >&6
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 12875 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    compiler_CXX \
+    CC_CXX \
+    LD_CXX \
+    lt_prog_compiler_wl_CXX \
+    lt_prog_compiler_pic_CXX \
+    lt_prog_compiler_static_CXX \
+    lt_prog_compiler_no_builtin_flag_CXX \
+    export_dynamic_flag_spec_CXX \
+    thread_safe_flag_spec_CXX \
+    whole_archive_flag_spec_CXX \
+    enable_shared_with_static_runtimes_CXX \
+    old_archive_cmds_CXX \
+    old_archive_from_new_cmds_CXX \
+    predep_objects_CXX \
+    postdep_objects_CXX \
+    predeps_CXX \
+    postdeps_CXX \
+    compiler_lib_search_path_CXX \
+    archive_cmds_CXX \
+    archive_expsym_cmds_CXX \
+    postinstall_cmds_CXX \
+    postuninstall_cmds_CXX \
+    old_archive_from_expsyms_cmds_CXX \
+    allow_undefined_flag_CXX \
+    no_undefined_flag_CXX \
+    export_symbols_cmds_CXX \
+    hardcode_libdir_flag_spec_CXX \
+    hardcode_libdir_flag_spec_ld_CXX \
+    hardcode_libdir_separator_CXX \
+    hardcode_automatic_CXX \
+    module_cmds_CXX \
+    module_expsym_cmds_CXX \
+    lt_cv_prog_compiler_c_o_CXX \
+    exclude_expsyms_CXX \
+    include_expsyms_CXX; do
+
+    case $var in
+    old_archive_cmds_CXX | \
+    old_archive_from_new_cmds_CXX | \
+    archive_cmds_CXX | \
+    archive_expsym_cmds_CXX | \
+    module_cmds_CXX | \
+    module_expsym_cmds_CXX | \
+    old_archive_from_expsyms_cmds_CXX | \
+    export_symbols_cmds_CXX | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\$0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+cfgfile="$ofile"
+
+  cat <<__EOF__ >> "$cfgfile"
+# ### BEGIN LIBTOOL TAG CONFIG: $tagname
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_CXX
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_compiler_CXX
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC_CXX
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_LD_CXX
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_CXX
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_CXX
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_CXX
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec_CXX
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds_CXX
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds_CXX
+archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_module_cmds_CXX
+module_expsym_cmds=$lt_module_expsym_cmds_CXX
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_predep_objects_CXX
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_postdep_objects_CXX
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_predeps_CXX
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_postdeps_CXX
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_CXX
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_CXX
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_CXX
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct_CXX
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L_CXX
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$hardcode_automatic_CXX
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_CXX
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path_CXX"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols_CXX
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_CXX
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_CXX
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_CXX
+
+# ### END LIBTOOL TAG CONFIG: $tagname
+
+__EOF__
+
+
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC=$lt_save_CC
+LDCXX=$LD
+LD=$lt_save_LD
+GCC=$lt_save_GCC
+with_gnu_ldcxx=$with_gnu_ld
+with_gnu_ld=$lt_save_with_gnu_ld
+lt_cv_path_LDCXX=$lt_cv_path_LD
+lt_cv_path_LD=$lt_save_path_LD
+lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+
+	else
+	  tagname=""
+	fi
+	;;
+
+      F77)
+	if test -n "$F77" && test "X$F77" != "Xno"; then
+
+ac_ext=f
+ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5'
+ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_f77_compiler_gnu
+
+
+archive_cmds_need_lc_F77=no
+allow_undefined_flag_F77=
+always_export_symbols_F77=no
+archive_expsym_cmds_F77=
+export_dynamic_flag_spec_F77=
+hardcode_direct_F77=no
+hardcode_libdir_flag_spec_F77=
+hardcode_libdir_flag_spec_ld_F77=
+hardcode_libdir_separator_F77=
+hardcode_minus_L_F77=no
+hardcode_automatic_F77=no
+module_cmds_F77=
+module_expsym_cmds_F77=
+link_all_deplibs_F77=unknown
+old_archive_cmds_F77=$old_archive_cmds
+no_undefined_flag_F77=
+whole_archive_flag_spec_F77=
+enable_shared_with_static_runtimes_F77=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+objext_F77=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="      subroutine t\n      return\n      end\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="      program t\n      end\n"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${F77-"f77"}
+compiler=$CC
+compiler_F77=$CC
+cc_basename=`$echo X"$compiler" | $Xsed -e 's%^.*/%%'`
+
+echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5
+echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $can_build_shared" >&5
+echo "${ECHO_T}$can_build_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build shared libraries" >&5
+echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6
+test "$can_build_shared" = "no" && enable_shared=no
+
+# On AIX, shared libraries and static libraries use the same namespace, and
+# are all built from PIC.
+case "$host_os" in
+aix3*)
+  test "$enable_shared" = yes && enable_static=no
+  if test -n "$RANLIB"; then
+    archive_cmds="$archive_cmds~\$RANLIB \$lib"
+    postinstall_cmds='$RANLIB $lib'
+  fi
+  ;;
+aix4* | aix5*)
+  test "$enable_shared" = yes && enable_static=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $enable_shared" >&5
+echo "${ECHO_T}$enable_shared" >&6
+
+echo "$as_me:$LINENO: checking whether to build static libraries" >&5
+echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6
+# Make sure either enable_shared or enable_static is yes.
+test "$enable_shared" = yes || enable_static=yes
+echo "$as_me:$LINENO: result: $enable_static" >&5
+echo "${ECHO_T}$enable_static" >&6
+
+test "$ld_shlibs_F77" = no && can_build_shared=no
+
+GCC_F77="$G77"
+LD_F77="$LD"
+
+lt_prog_compiler_wl_F77=
+lt_prog_compiler_pic_F77=
+lt_prog_compiler_static_F77=
+
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl_F77='-Wl,'
+    lt_prog_compiler_static_F77='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_F77='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_prog_compiler_pic_F77='-m68020 -resident32 -malways-restore-a4'
+      ;;
+
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic_F77='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic_F77='-fno-common'
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared_F77=no
+      enable_shared=no
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic_F77=-Kconform_pic
+      fi
+      ;;
+
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic_F77='-fPIC'
+	;;
+      esac
+      ;;
+
+    *)
+      lt_prog_compiler_pic_F77='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_F77='-Bstatic'
+      else
+	lt_prog_compiler_static_F77='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic_F77='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic_F77='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static_F77='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static_F77='-non_shared'
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic_F77='-KPIC'
+      lt_prog_compiler_static_F77='-Bstatic'
+      ;;
+
+    linux*)
+      case $CC in
+      icc* | ecc*)
+	lt_prog_compiler_wl_F77='-Wl,'
+	lt_prog_compiler_pic_F77='-KPIC'
+	lt_prog_compiler_static_F77='-static'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl_F77='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static_F77='-non_shared'
+        ;;
+      esac
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static_F77='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      lt_prog_compiler_pic_F77='-Kpic'
+      lt_prog_compiler_static_F77='-dn'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      lt_prog_compiler_pic_F77='-KPIC'
+      lt_prog_compiler_static_F77='-Bstatic'
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl_F77='-Qoption ld '
+      lt_prog_compiler_pic_F77='-PIC'
+      lt_prog_compiler_static_F77='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      lt_prog_compiler_wl_F77='-Wl,'
+      lt_prog_compiler_pic_F77='-KPIC'
+      lt_prog_compiler_static_F77='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_prog_compiler_pic_F77='-Kconform_pic'
+	lt_prog_compiler_static_F77='-Bstatic'
+      fi
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic_F77='-pic'
+      lt_prog_compiler_static_F77='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared_F77=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_F77" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_F77" >&6
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic_F77"; then
+
+echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works... $ECHO_C" >&6
+if test "${lt_prog_compiler_pic_works_F77+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_prog_compiler_pic_works_F77=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic_F77"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:13702: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:13706: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_prog_compiler_pic_works_F77=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_F77" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_works_F77" >&6
+
+if test x"$lt_prog_compiler_pic_works_F77" = xyes; then
+    case $lt_prog_compiler_pic_F77 in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic_F77=" $lt_prog_compiler_pic_F77" ;;
+     esac
+else
+    lt_prog_compiler_pic_F77=
+     lt_prog_compiler_can_build_shared_F77=no
+fi
+
+fi
+case "$host_os" in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic_F77=
+    ;;
+  *)
+    lt_prog_compiler_pic_F77="$lt_prog_compiler_pic_F77"
+    ;;
+esac
+
+echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o_F77+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_c_o_F77=no
+   $rm -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:13762: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:13766: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s out/conftest.err; then
+       lt_cv_prog_compiler_c_o_F77=yes
+     fi
+   fi
+   chmod u+w .
+   $rm conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
+   $rm out/* && rmdir out
+   cd ..
+   rmdir conftest
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_F77" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o_F77" >&6
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o_F77" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+  if test "$hard_links" = no; then
+    { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+  runpath_var=
+  allow_undefined_flag_F77=
+  enable_shared_with_static_runtimes_F77=no
+  archive_cmds_F77=
+  archive_expsym_cmds_F77=
+  old_archive_From_new_cmds_F77=
+  old_archive_from_expsyms_cmds_F77=
+  export_dynamic_flag_spec_F77=
+  whole_archive_flag_spec_F77=
+  thread_safe_flag_spec_F77=
+  hardcode_libdir_flag_spec_F77=
+  hardcode_libdir_flag_spec_ld_F77=
+  hardcode_libdir_separator_F77=
+  hardcode_direct_F77=no
+  hardcode_minus_L_F77=no
+  hardcode_shlibpath_var_F77=unsupported
+  link_all_deplibs_F77=unknown
+  hardcode_automatic_F77=no
+  module_cmds_F77=
+  module_expsym_cmds_F77=
+  always_export_symbols_F77=no
+  export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms_F77=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms_F77="_GLOBAL_OFFSET_TABLE_"
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  ld_shlibs_F77=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	ld_shlibs_F77=no
+	cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_minus_L_F77=yes
+
+      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we can't use
+      # them.
+      ld_shlibs_F77=no
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag_F77=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds_F77='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	ld_shlibs_F77=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, F77) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      allow_undefined_flag_F77=unsupported
+      always_export_symbols_F77=no
+      enable_shared_with_static_runtimes_F77=yes
+      export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	archive_expsym_cmds_F77='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000  ${wl}--out-implib,$lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds_F77='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris* | sysv5*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+	ld_shlibs_F77=no
+	cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs_F77=no
+      fi
+      ;;
+
+    sunos4*)
+      archive_cmds_F77='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+  linux*)
+    if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_cmds_F77="$tmp_archive_cmds"
+      supports_anon_versioning=no
+      case `$LD -v 2>/dev/null` in
+        *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+        *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+        *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+        *\ 2.11.*) ;; # other 2.11 versions
+        *) supports_anon_versioning=yes ;;
+      esac
+      if test $supports_anon_versioning = yes; then
+        archive_expsym_cmds_F77='$echo "{ global:" > $output_objdir/$libname.ver~
+cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+$echo "local: *; };" >> $output_objdir/$libname.ver~
+        $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+      else
+        archive_expsym_cmds_F77="$tmp_archive_cmds"
+      fi
+      link_all_deplibs_F77=no
+    else
+      ld_shlibs_F77=no
+    fi
+    ;;
+
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs_F77=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs_F77" = yes; then
+      runpath_var=LD_RUN_PATH
+      hardcode_libdir_flag_spec_F77='${wl}--rpath ${wl}$libdir'
+      export_dynamic_flag_spec_F77='${wl}--export-dynamic'
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then
+ 	whole_archive_flag_spec_F77="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+  	whole_archive_flag_spec_F77=
+      fi
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag_F77=unsupported
+      always_export_symbols_F77=yes
+      archive_expsym_cmds_F77='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L_F77=yes
+      if test "$GCC" = yes && test -z "$link_static_flag"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct_F77=unsupported
+      fi
+      ;;
+
+    aix4* | aix5*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+	  export_symbols_cmds_F77='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds_F77='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+	  for ld_flag in $LDFLAGS; do
+  	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+  	    aix_use_runtimelinking=yes
+  	    break
+  	  fi
+	  done
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds_F77=''
+      hardcode_direct_F77=yes
+      hardcode_libdir_separator_F77=':'
+      link_all_deplibs_F77=yes
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.012|aix4.012.*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" && \
+  	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	  then
+  	  # We have reworked collect2
+  	  hardcode_direct_F77=yes
+	  else
+  	  # We have old collect2
+  	  hardcode_direct_F77=unsupported
+  	  # It fails to find uninstalled libraries when the uninstalled
+  	  # path is not listed in the libpath.  Setting hardcode_minus_L
+  	  # to unsupported forces relinking
+  	  hardcode_minus_L_F77=yes
+  	  hardcode_libdir_flag_spec_F77='-L$libdir'
+  	  hardcode_libdir_separator_F77=
+	  fi
+	esac
+	shared_flag='-shared'
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+  	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+  	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+  	if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+  	fi
+	fi
+      fi
+
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols_F77=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag_F77='-berok'
+       # Determine the default libpath from the value encoded in an empty executable.
+       cat >conftest.$ac_ext <<_ACEOF
+      program main
+
+      end
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_f77_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+       hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath"
+	archive_expsym_cmds_F77="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+       else
+	if test "$host_cpu" = ia64; then
+	  hardcode_libdir_flag_spec_F77='${wl}-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag_F77="-z nodefs"
+	  archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an empty executable.
+	 cat >conftest.$ac_ext <<_ACEOF
+      program main
+
+      end
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_f77_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+	 hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag_F77=' ${wl}-bernotok'
+	  allow_undefined_flag_F77=' ${wl}-berok'
+	  # -bexpall does not export symbols beginning with underscore (_)
+	  always_export_symbols_F77=yes
+	  # Exported symbols can be pulled into shared objects from archives
+	  whole_archive_flag_spec_F77=' '
+	  archive_cmds_need_lc_F77=yes
+	  # This is similar to how AIX traditionally builds it's shared libraries.
+	  archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_minus_L_F77=yes
+      # see comment about different semantics on the GNU ld section
+      ld_shlibs_F77=no
+      ;;
+
+    bsdi4*)
+      export_dynamic_flag_spec_F77=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec_F77=' '
+      allow_undefined_flag_F77=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      archive_cmds_F77='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      old_archive_From_new_cmds_F77='true'
+      # FIXME: Should let the user specify the lib program.
+      old_archive_cmds_F77='lib /OUT:$oldlib$oldobjs$old_deplibs'
+      fix_srcfile_path='`cygpath -w "$srcfile"`'
+      enable_shared_with_static_runtimes_F77=yes
+      ;;
+
+    darwin* | rhapsody*)
+    if test "$GXX" = yes ; then
+      archive_cmds_need_lc_F77=no
+      case "$host_os" in
+      rhapsody* | darwin1.[012])
+	allow_undefined_flag_F77='-undefined suppress'
+	;;
+      *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	allow_undefined_flag_F77='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[012])
+            allow_undefined_flag_F77='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            allow_undefined_flag_F77='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+	;;
+      esac
+    	lt_int_apple_cc_single_mod=no
+    	output_verbose_link_cmd='echo'
+    	if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+    	  lt_int_apple_cc_single_mod=yes
+    	fi
+    	if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+    	  archive_cmds_F77='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    	else
+        archive_cmds_F77='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+      fi
+      module_cmds_F77='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+      # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+        if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+          archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        else
+          archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        fi
+          module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+      hardcode_direct_F77=no
+      hardcode_automatic_F77=yes
+      hardcode_shlibpath_var_F77=unsupported
+      whole_archive_flag_spec_F77='-all_load $convenience'
+      link_all_deplibs_F77=yes
+    else
+      ld_shlibs_F77=no
+    fi
+      ;;
+
+    dgux*)
+      archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    freebsd1*)
+      ld_shlibs_F77=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec_F77='-R$libdir'
+      hardcode_direct_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_F77=yes
+      hardcode_minus_L_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | kfreebsd*-gnu)
+      archive_cmds_F77='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec_F77='-R$libdir'
+      hardcode_direct_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	archive_cmds_F77='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds_F77='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator_F77=:
+      hardcode_direct_F77=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L_F77=yes
+      export_dynamic_flag_spec_F77='${wl}-E'
+      ;;
+
+    hpux10* | hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds_F77='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	*)
+	  archive_cmds_F77='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*)
+	  hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_flag_spec_ld_F77='+b $libdir'
+	  hardcode_libdir_separator_F77=:
+	  hardcode_direct_F77=no
+	  hardcode_shlibpath_var_F77=no
+	  ;;
+	ia64*)
+	  hardcode_libdir_flag_spec_F77='-L$libdir'
+	  hardcode_direct_F77=no
+	  hardcode_shlibpath_var_F77=no
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L_F77=yes
+	  ;;
+	*)
+	  hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_separator_F77=:
+	  hardcode_direct_F77=yes
+	  export_dynamic_flag_spec_F77='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L_F77=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	archive_cmds_F77='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec_ld_F77='-rpath $libdir'
+      fi
+      hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_F77=:
+      link_all_deplibs_F77=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds_F77='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec_F77='-R$libdir'
+      hardcode_direct_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    newsos6)
+      archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_F77=yes
+      hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_F77=:
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    openbsd*)
+      hardcode_direct_F77=yes
+      hardcode_shlibpath_var_F77=no
+      if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir'
+	export_dynamic_flag_spec_F77='${wl}-E'
+      else
+       case $host_os in
+	 openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	   archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	   hardcode_libdir_flag_spec_F77='-R$libdir'
+	   ;;
+	 *)
+	   archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	   hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir'
+	   ;;
+       esac
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_minus_L_F77=yes
+      allow_undefined_flag_F77=unsupported
+      archive_cmds_F77='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_From_new_cmds_F77='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	allow_undefined_flag_F77=' -expect_unresolved \*'
+	archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_F77=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir'
+      else
+	allow_undefined_flag_F77=' -expect_unresolved \*'
+	archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds_F77='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+	$LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec_F77='-rpath $libdir'
+      fi
+      hardcode_libdir_separator_F77=:
+      ;;
+
+    sco3.2v5*)
+      archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var_F77=no
+      export_dynamic_flag_spec_F77='${wl}-Bexport'
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ;;
+
+    solaris*)
+      no_undefined_flag_F77=' -z text'
+      if test "$GCC" = yes; then
+	archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	  $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp'
+      else
+	archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  	$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      fi
+      hardcode_libdir_flag_spec_F77='-R$libdir'
+      hardcode_shlibpath_var_F77=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+	whole_archive_flag_spec_F77='-z allextract$convenience -z defaultextract' ;;
+      esac
+      link_all_deplibs_F77=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds_F77='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds_F77='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_direct_F77=yes
+      hardcode_minus_L_F77=yes
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct_F77=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds_F77='$CC -r -o $output$reload_objs'
+	  hardcode_direct_F77=no
+        ;;
+	motorola)
+	  archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct_F77=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var_F77=no
+      export_dynamic_flag_spec_F77='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var_F77=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs_F77=yes
+      fi
+      ;;
+
+    sysv4.2uw2*)
+      archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_F77=yes
+      hardcode_minus_L_F77=no
+      hardcode_shlibpath_var_F77=no
+      hardcode_runpath_var=yes
+      runpath_var=LD_RUN_PATH
+      ;;
+
+   sysv5OpenUNIX8* | sysv5UnixWare7* |  sysv5uw[78]* | unixware7*)
+      no_undefined_flag_F77='${wl}-z ${wl}text'
+      if test "$GCC" = yes; then
+	archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds_F77='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    sysv5*)
+      no_undefined_flag_F77=' -z text'
+      # $CC -shared without GNU ld will not create a library from C++
+      # object files and a static libstdc++, better avoid it by now
+      archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      hardcode_libdir_flag_spec_F77=
+      hardcode_shlibpath_var_F77=no
+      runpath_var='LD_RUN_PATH'
+      ;;
+
+    uts4*)
+      archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec_F77='-L$libdir'
+      hardcode_shlibpath_var_F77=no
+      ;;
+
+    *)
+      ld_shlibs_F77=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $ld_shlibs_F77" >&5
+echo "${ECHO_T}$ld_shlibs_F77" >&6
+test "$ld_shlibs_F77" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc_F77" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc_F77=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds_F77 in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+      $rm conftest*
+      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$lt_prog_compiler_wl_F77
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$allow_undefined_flag_F77
+        allow_undefined_flag_F77=
+        if { (eval echo "$as_me:$LINENO: \"$archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+        then
+	  archive_cmds_need_lc_F77=no
+        else
+	  archive_cmds_need_lc_F77=yes
+        fi
+        allow_undefined_flag_F77=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $rm conftest*
+      echo "$as_me:$LINENO: result: $archive_cmds_need_lc_F77" >&5
+echo "${ECHO_T}$archive_cmds_need_lc_F77" >&6
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+if test "$GCC" = yes; then
+  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
+  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
+  if test "$GCC" = yes; then
+    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
+  else
+    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
+  fi
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+kfreebsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.01* | freebsdelf3.01*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  *) # from 3.2 on
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case "$host_cpu" in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+   hppa*64*)
+     shrext_cmds='.sl'
+     hardcode_into_libs=yes
+     dynamic_linker="$host_os dld.sl"
+     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+     soname_spec='${libname}${release}${shared_ext}$major'
+     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+     ;;
+   *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+knetbsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+nto-qnx*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=yes
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action_F77=
+if test -n "$hardcode_libdir_flag_spec_F77" || \
+   test -n "$runpath_var F77" || \
+   test "X$hardcode_automatic_F77"="Xyes" ; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct_F77" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, F77)" != no &&
+     test "$hardcode_minus_L_F77" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action_F77=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action_F77=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action_F77=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action_F77" >&5
+echo "${ECHO_T}$hardcode_action_F77" >&6
+
+if test "$hardcode_action_F77" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+   darwin*)
+       if test -n "$STRIP" ; then
+         striplib="$STRIP -x"
+         echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+       else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+       ;;
+   *)
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+    ;;
+  esac
+fi
+
+
+
+# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    compiler_F77 \
+    CC_F77 \
+    LD_F77 \
+    lt_prog_compiler_wl_F77 \
+    lt_prog_compiler_pic_F77 \
+    lt_prog_compiler_static_F77 \
+    lt_prog_compiler_no_builtin_flag_F77 \
+    export_dynamic_flag_spec_F77 \
+    thread_safe_flag_spec_F77 \
+    whole_archive_flag_spec_F77 \
+    enable_shared_with_static_runtimes_F77 \
+    old_archive_cmds_F77 \
+    old_archive_from_new_cmds_F77 \
+    predep_objects_F77 \
+    postdep_objects_F77 \
+    predeps_F77 \
+    postdeps_F77 \
+    compiler_lib_search_path_F77 \
+    archive_cmds_F77 \
+    archive_expsym_cmds_F77 \
+    postinstall_cmds_F77 \
+    postuninstall_cmds_F77 \
+    old_archive_from_expsyms_cmds_F77 \
+    allow_undefined_flag_F77 \
+    no_undefined_flag_F77 \
+    export_symbols_cmds_F77 \
+    hardcode_libdir_flag_spec_F77 \
+    hardcode_libdir_flag_spec_ld_F77 \
+    hardcode_libdir_separator_F77 \
+    hardcode_automatic_F77 \
+    module_cmds_F77 \
+    module_expsym_cmds_F77 \
+    lt_cv_prog_compiler_c_o_F77 \
+    exclude_expsyms_F77 \
+    include_expsyms_F77; do
+
+    case $var in
+    old_archive_cmds_F77 | \
+    old_archive_from_new_cmds_F77 | \
+    archive_cmds_F77 | \
+    archive_expsym_cmds_F77 | \
+    module_cmds_F77 | \
+    module_expsym_cmds_F77 | \
+    old_archive_from_expsyms_cmds_F77 | \
+    export_symbols_cmds_F77 | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\$0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+cfgfile="$ofile"
+
+  cat <<__EOF__ >> "$cfgfile"
+# ### BEGIN LIBTOOL TAG CONFIG: $tagname
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_F77
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_F77
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_compiler_F77
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC_F77
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_LD_F77
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_F77
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_F77
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_F77
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_F77
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_F77
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_F77
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_F77
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec_F77
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds_F77
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_F77
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_F77
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds_F77
+archive_expsym_cmds=$lt_archive_expsym_cmds_F77
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_module_cmds_F77
+module_expsym_cmds=$lt_module_expsym_cmds_F77
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_predep_objects_F77
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_postdep_objects_F77
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_predeps_F77
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_postdeps_F77
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_F77
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_F77
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_F77
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_F77
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_F77
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_F77
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_F77
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct_F77
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L_F77
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_F77
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$hardcode_automatic_F77
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_F77
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path_F77"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols_F77
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_F77
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_F77
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_F77
+
+# ### END LIBTOOL TAG CONFIG: $tagname
+
+__EOF__
+
+
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+	else
+	  tagname=""
+	fi
+	;;
+
+      GCJ)
+	if test -n "$GCJ" && test "X$GCJ" != "Xno"; then
+
+
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+objext_GCJ=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}\n"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String argv) {}; }\n'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${GCJ-"gcj"}
+compiler=$CC
+compiler_GCJ=$CC
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+archive_cmds_need_lc_GCJ=no
+
+
+lt_prog_compiler_no_builtin_flag_GCJ=
+
+if test "$GCC" = yes; then
+  lt_prog_compiler_no_builtin_flag_GCJ=' -fno-builtin'
+
+
+echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:15807: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:15811: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag_GCJ="$lt_prog_compiler_no_builtin_flag_GCJ -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+lt_prog_compiler_wl_GCJ=
+lt_prog_compiler_pic_GCJ=
+lt_prog_compiler_static_GCJ=
+
+echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5
+echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl_GCJ='-Wl,'
+    lt_prog_compiler_static_GCJ='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_GCJ='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      # FIXME: we need at least 68020 code to build shared libraries, but
+      # adding the `-m68020' flag to GCC prevents building anything better,
+      # like `-m68040'.
+      lt_prog_compiler_pic_GCJ='-m68020 -resident32 -malways-restore-a4'
+      ;;
+
+    beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic_GCJ='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic_GCJ='-fno-common'
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared_GCJ=no
+      enable_shared=no
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic_GCJ=-Kconform_pic
+      fi
+      ;;
+
+    hpux*)
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic_GCJ='-fPIC'
+	;;
+      esac
+      ;;
+
+    *)
+      lt_prog_compiler_pic_GCJ='-fPIC'
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static_GCJ='-Bstatic'
+      else
+	lt_prog_compiler_static_GCJ='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | pw32* | os2*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic_GCJ='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case "$host_cpu" in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic_GCJ='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static_GCJ='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static_GCJ='-non_shared'
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic_GCJ='-KPIC'
+      lt_prog_compiler_static_GCJ='-Bstatic'
+      ;;
+
+    linux*)
+      case $CC in
+      icc* | ecc*)
+	lt_prog_compiler_wl_GCJ='-Wl,'
+	lt_prog_compiler_pic_GCJ='-KPIC'
+	lt_prog_compiler_static_GCJ='-static'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl_GCJ='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static_GCJ='-non_shared'
+        ;;
+      esac
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static_GCJ='-non_shared'
+      ;;
+
+    sco3.2v5*)
+      lt_prog_compiler_pic_GCJ='-Kpic'
+      lt_prog_compiler_static_GCJ='-dn'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      lt_prog_compiler_pic_GCJ='-KPIC'
+      lt_prog_compiler_static_GCJ='-Bstatic'
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl_GCJ='-Qoption ld '
+      lt_prog_compiler_pic_GCJ='-PIC'
+      lt_prog_compiler_static_GCJ='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+      lt_prog_compiler_wl_GCJ='-Wl,'
+      lt_prog_compiler_pic_GCJ='-KPIC'
+      lt_prog_compiler_static_GCJ='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_prog_compiler_pic_GCJ='-Kconform_pic'
+	lt_prog_compiler_static_GCJ='-Bstatic'
+      fi
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic_GCJ='-pic'
+      lt_prog_compiler_static_GCJ='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared_GCJ=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_GCJ" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_GCJ" >&6
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic_GCJ"; then
+
+echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works" >&5
+echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works... $ECHO_C" >&6
+if test "${lt_prog_compiler_pic_works_GCJ+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_prog_compiler_pic_works_GCJ=no
+  ac_outfile=conftest.$ac_objext
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic_GCJ"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:16040: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:16044: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s conftest.err; then
+       lt_prog_compiler_pic_works_GCJ=yes
+     fi
+   fi
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_GCJ" >&5
+echo "${ECHO_T}$lt_prog_compiler_pic_works_GCJ" >&6
+
+if test x"$lt_prog_compiler_pic_works_GCJ" = xyes; then
+    case $lt_prog_compiler_pic_GCJ in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic_GCJ=" $lt_prog_compiler_pic_GCJ" ;;
+     esac
+else
+    lt_prog_compiler_pic_GCJ=
+     lt_prog_compiler_can_build_shared_GCJ=no
+fi
+
+fi
+case "$host_os" in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic_GCJ=
+    ;;
+  *)
+    lt_prog_compiler_pic_GCJ="$lt_prog_compiler_pic_GCJ"
+    ;;
+esac
+
+echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5
+echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6
+if test "${lt_cv_prog_compiler_c_o_GCJ+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  lt_cv_prog_compiler_c_o_GCJ=no
+   $rm -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:16100: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:16104: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test ! -s out/conftest.err; then
+       lt_cv_prog_compiler_c_o_GCJ=yes
+     fi
+   fi
+   chmod u+w .
+   $rm conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files
+   $rm out/* && rmdir out
+   cd ..
+   rmdir conftest
+   $rm conftest*
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_GCJ" >&5
+echo "${ECHO_T}$lt_cv_prog_compiler_c_o_GCJ" >&6
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o_GCJ" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  echo "$as_me:$LINENO: checking if we can lock with hard links" >&5
+echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6
+  hard_links=yes
+  $rm conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  echo "$as_me:$LINENO: result: $hard_links" >&5
+echo "${ECHO_T}$hard_links" >&6
+  if test "$hard_links" = no; then
+    { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6
+
+  runpath_var=
+  allow_undefined_flag_GCJ=
+  enable_shared_with_static_runtimes_GCJ=no
+  archive_cmds_GCJ=
+  archive_expsym_cmds_GCJ=
+  old_archive_From_new_cmds_GCJ=
+  old_archive_from_expsyms_cmds_GCJ=
+  export_dynamic_flag_spec_GCJ=
+  whole_archive_flag_spec_GCJ=
+  thread_safe_flag_spec_GCJ=
+  hardcode_libdir_flag_spec_GCJ=
+  hardcode_libdir_flag_spec_ld_GCJ=
+  hardcode_libdir_separator_GCJ=
+  hardcode_direct_GCJ=no
+  hardcode_minus_L_GCJ=no
+  hardcode_shlibpath_var_GCJ=unsupported
+  link_all_deplibs_GCJ=unknown
+  hardcode_automatic_GCJ=no
+  module_cmds_GCJ=
+  module_expsym_cmds_GCJ=
+  always_export_symbols_GCJ=no
+  export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms_GCJ=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms_GCJ="_GLOBAL_OFFSET_TABLE_"
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  ld_shlibs_GCJ=yes
+  if test "$with_gnu_ld" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix3* | aix4* | aix5*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	ld_shlibs_GCJ=no
+	cat <<EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+EOF
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_minus_L_GCJ=yes
+
+      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we can't use
+      # them.
+      ld_shlibs_GCJ=no
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag_GCJ=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds_GCJ='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	ld_shlibs_GCJ=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, GCJ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      allow_undefined_flag_GCJ=unsupported
+      always_export_symbols_GCJ=no
+      enable_shared_with_static_runtimes_GCJ=yes
+      export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols'
+
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	archive_expsym_cmds_GCJ='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000  ${wl}--out-implib,$lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds_GCJ='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris* | sysv5*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+	ld_shlibs_GCJ=no
+	cat <<EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+EOF
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs_GCJ=no
+      fi
+      ;;
+
+    sunos4*)
+      archive_cmds_GCJ='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+  linux*)
+    if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        tmp_archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_cmds_GCJ="$tmp_archive_cmds"
+      supports_anon_versioning=no
+      case `$LD -v 2>/dev/null` in
+        *\ 01.* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+        *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+        *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+        *\ 2.11.*) ;; # other 2.11 versions
+        *) supports_anon_versioning=yes ;;
+      esac
+      if test $supports_anon_versioning = yes; then
+        archive_expsym_cmds_GCJ='$echo "{ global:" > $output_objdir/$libname.ver~
+cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+$echo "local: *; };" >> $output_objdir/$libname.ver~
+        $CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+      else
+        archive_expsym_cmds_GCJ="$tmp_archive_cmds"
+      fi
+      link_all_deplibs_GCJ=no
+    else
+      ld_shlibs_GCJ=no
+    fi
+    ;;
+
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+	archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs_GCJ=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs_GCJ" = yes; then
+      runpath_var=LD_RUN_PATH
+      hardcode_libdir_flag_spec_GCJ='${wl}--rpath ${wl}$libdir'
+      export_dynamic_flag_spec_GCJ='${wl}--export-dynamic'
+      # ancient GNU ld didn't support --whole-archive et. al.
+      if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then
+ 	whole_archive_flag_spec_GCJ="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+      else
+  	whole_archive_flag_spec_GCJ=
+      fi
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag_GCJ=unsupported
+      always_export_symbols_GCJ=yes
+      archive_expsym_cmds_GCJ='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L_GCJ=yes
+      if test "$GCC" = yes && test -z "$link_static_flag"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct_GCJ=unsupported
+      fi
+      ;;
+
+    aix4* | aix5*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	if $NM -V 2>&1 | grep 'GNU' > /dev/null; then
+	  export_symbols_cmds_GCJ='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds_GCJ='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+	  for ld_flag in $LDFLAGS; do
+  	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+  	    aix_use_runtimelinking=yes
+  	    break
+  	  fi
+	  done
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds_GCJ=''
+      hardcode_direct_GCJ=yes
+      hardcode_libdir_separator_GCJ=':'
+      link_all_deplibs_GCJ=yes
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.012|aix4.012.*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" && \
+  	   strings "$collect2name" | grep resolve_lib_name >/dev/null
+	  then
+  	  # We have reworked collect2
+  	  hardcode_direct_GCJ=yes
+	  else
+  	  # We have old collect2
+  	  hardcode_direct_GCJ=unsupported
+  	  # It fails to find uninstalled libraries when the uninstalled
+  	  # path is not listed in the libpath.  Setting hardcode_minus_L
+  	  # to unsupported forces relinking
+  	  hardcode_minus_L_GCJ=yes
+  	  hardcode_libdir_flag_spec_GCJ='-L$libdir'
+  	  hardcode_libdir_separator_GCJ=
+	  fi
+	esac
+	shared_flag='-shared'
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+  	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+  	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+  	if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+  	fi
+	fi
+      fi
+
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols_GCJ=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag_GCJ='-berok'
+       # Determine the default libpath from the value encoded in an empty executable.
+       cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+       hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath"
+	archive_expsym_cmds_GCJ="\$CC"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+       else
+	if test "$host_cpu" = ia64; then
+	  hardcode_libdir_flag_spec_GCJ='${wl}-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag_GCJ="-z nodefs"
+	  archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$no_entry_flag \${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an empty executable.
+	 cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`; fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+	 hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag_GCJ=' ${wl}-bernotok'
+	  allow_undefined_flag_GCJ=' ${wl}-berok'
+	  # -bexpall does not export symbols beginning with underscore (_)
+	  always_export_symbols_GCJ=yes
+	  # Exported symbols can be pulled into shared objects from archives
+	  whole_archive_flag_spec_GCJ=' '
+	  archive_cmds_need_lc_GCJ=yes
+	  # This is similar to how AIX traditionally builds it's shared libraries.
+	  archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs $compiler_flags ${wl}-bE:$export_symbols ${wl}-bnoentry${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_minus_L_GCJ=yes
+      # see comment about different semantics on the GNU ld section
+      ld_shlibs_GCJ=no
+      ;;
+
+    bsdi4*)
+      export_dynamic_flag_spec_GCJ=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec_GCJ=' '
+      allow_undefined_flag_GCJ=unsupported
+      # Tell ltmain to make .lib files, not .a files.
+      libext=lib
+      # Tell ltmain to make .dll files, not .so files.
+      shrext_cmds=".dll"
+      # FIXME: Setting linknames here is a bad hack.
+      archive_cmds_GCJ='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames='
+      # The linker will automatically build a .lib file if we build a DLL.
+      old_archive_From_new_cmds_GCJ='true'
+      # FIXME: Should let the user specify the lib program.
+      old_archive_cmds_GCJ='lib /OUT:$oldlib$oldobjs$old_deplibs'
+      fix_srcfile_path='`cygpath -w "$srcfile"`'
+      enable_shared_with_static_runtimes_GCJ=yes
+      ;;
+
+    darwin* | rhapsody*)
+    if test "$GXX" = yes ; then
+      archive_cmds_need_lc_GCJ=no
+      case "$host_os" in
+      rhapsody* | darwin1.[012])
+	allow_undefined_flag_GCJ='-undefined suppress'
+	;;
+      *) # Darwin 1.3 on
+      if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then
+      	allow_undefined_flag_GCJ='-flat_namespace -undefined suppress'
+      else
+        case ${MACOSX_DEPLOYMENT_TARGET} in
+          10.[012])
+            allow_undefined_flag_GCJ='-flat_namespace -undefined suppress'
+            ;;
+          10.*)
+            allow_undefined_flag_GCJ='-undefined dynamic_lookup'
+            ;;
+        esac
+      fi
+	;;
+      esac
+    	lt_int_apple_cc_single_mod=no
+    	output_verbose_link_cmd='echo'
+    	if $CC -dumpspecs 2>&1 | grep 'single_module' >/dev/null ; then
+    	  lt_int_apple_cc_single_mod=yes
+    	fi
+    	if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+    	  archive_cmds_GCJ='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+    	else
+        archive_cmds_GCJ='$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring'
+      fi
+      module_cmds_GCJ='$CC ${wl}-bind_at_load $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags'
+      # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin ld's
+        if test "X$lt_int_apple_cc_single_mod" = Xyes ; then
+          archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        else
+          archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r ${wl}-bind_at_load -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+        fi
+          module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[    ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag  -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}'
+      hardcode_direct_GCJ=no
+      hardcode_automatic_GCJ=yes
+      hardcode_shlibpath_var_GCJ=unsupported
+      whole_archive_flag_spec_GCJ='-all_load $convenience'
+      link_all_deplibs_GCJ=yes
+    else
+      ld_shlibs_GCJ=no
+    fi
+      ;;
+
+    dgux*)
+      archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    freebsd1*)
+      ld_shlibs_GCJ=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec_GCJ='-R$libdir'
+      hardcode_direct_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2*)
+      archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_GCJ=yes
+      hardcode_minus_L_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | kfreebsd*-gnu)
+      archive_cmds_GCJ='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec_GCJ='-R$libdir'
+      hardcode_direct_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	archive_cmds_GCJ='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds_GCJ='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator_GCJ=:
+      hardcode_direct_GCJ=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L_GCJ=yes
+      export_dynamic_flag_spec_GCJ='${wl}-E'
+      ;;
+
+    hpux10* | hpux11*)
+      if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case "$host_cpu" in
+	hppa*64*|ia64*)
+	  archive_cmds_GCJ='$LD -b +h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	*)
+	  archive_cmds_GCJ='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	case "$host_cpu" in
+	hppa*64*)
+	  hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_flag_spec_ld_GCJ='+b $libdir'
+	  hardcode_libdir_separator_GCJ=:
+	  hardcode_direct_GCJ=no
+	  hardcode_shlibpath_var_GCJ=no
+	  ;;
+	ia64*)
+	  hardcode_libdir_flag_spec_GCJ='-L$libdir'
+	  hardcode_direct_GCJ=no
+	  hardcode_shlibpath_var_GCJ=no
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L_GCJ=yes
+	  ;;
+	*)
+	  hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir'
+	  hardcode_libdir_separator_GCJ=:
+	  hardcode_direct_GCJ=yes
+	  export_dynamic_flag_spec_GCJ='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L_GCJ=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	archive_cmds_GCJ='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec_ld_GCJ='-rpath $libdir'
+      fi
+      hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_GCJ=:
+      link_all_deplibs_GCJ=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu | knetbsd*-gnu)
+      if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+	archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds_GCJ='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec_GCJ='-R$libdir'
+      hardcode_direct_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    newsos6)
+      archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_GCJ=yes
+      hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_GCJ=:
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    openbsd*)
+      hardcode_direct_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir'
+	export_dynamic_flag_spec_GCJ='${wl}-E'
+      else
+       case $host_os in
+	 openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	   archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	   hardcode_libdir_flag_spec_GCJ='-R$libdir'
+	   ;;
+	 *)
+	   archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	   hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir'
+	   ;;
+       esac
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_minus_L_GCJ=yes
+      allow_undefined_flag_GCJ=unsupported
+      archive_cmds_GCJ='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_From_new_cmds_GCJ='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	allow_undefined_flag_GCJ=' -expect_unresolved \*'
+	archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator_GCJ=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir'
+      else
+	allow_undefined_flag_GCJ=' -expect_unresolved \*'
+	archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds_GCJ='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~
+	$LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${objdir}/so_locations -o $lib~$rm $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec_GCJ='-rpath $libdir'
+      fi
+      hardcode_libdir_separator_GCJ=:
+      ;;
+
+    sco3.2v5*)
+      archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var_GCJ=no
+      export_dynamic_flag_spec_GCJ='${wl}-Bexport'
+      runpath_var=LD_RUN_PATH
+      hardcode_runpath_var=yes
+      ;;
+
+    solaris*)
+      no_undefined_flag_GCJ=' -z text'
+      if test "$GCC" = yes; then
+	archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+	  $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp'
+      else
+	archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  	$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      fi
+      hardcode_libdir_flag_spec_GCJ='-R$libdir'
+      hardcode_shlibpath_var_GCJ=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *) # Supported since Solaris 2.6 (maybe 2.5.1?)
+	whole_archive_flag_spec_GCJ='-z allextract$convenience -z defaultextract' ;;
+      esac
+      link_all_deplibs_GCJ=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds_GCJ='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds_GCJ='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_direct_GCJ=yes
+      hardcode_minus_L_GCJ=yes
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct_GCJ=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds_GCJ='$CC -r -o $output$reload_objs'
+	  hardcode_direct_GCJ=no
+        ;;
+	motorola)
+	  archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct_GCJ=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var_GCJ=no
+      export_dynamic_flag_spec_GCJ='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var_GCJ=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs_GCJ=yes
+      fi
+      ;;
+
+    sysv4.2uw2*)
+      archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct_GCJ=yes
+      hardcode_minus_L_GCJ=no
+      hardcode_shlibpath_var_GCJ=no
+      hardcode_runpath_var=yes
+      runpath_var=LD_RUN_PATH
+      ;;
+
+   sysv5OpenUNIX8* | sysv5UnixWare7* |  sysv5uw[78]* | unixware7*)
+      no_undefined_flag_GCJ='${wl}-z ${wl}text'
+      if test "$GCC" = yes; then
+	archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds_GCJ='$CC -G ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    sysv5*)
+      no_undefined_flag_GCJ=' -z text'
+      # $CC -shared without GNU ld will not create a library from C++
+      # object files and a static libstdc++, better avoid it by now
+      archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~
+  		$LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp'
+      hardcode_libdir_flag_spec_GCJ=
+      hardcode_shlibpath_var_GCJ=no
+      runpath_var='LD_RUN_PATH'
+      ;;
+
+    uts4*)
+      archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec_GCJ='-L$libdir'
+      hardcode_shlibpath_var_GCJ=no
+      ;;
+
+    *)
+      ld_shlibs_GCJ=no
+      ;;
+    esac
+  fi
+
+echo "$as_me:$LINENO: result: $ld_shlibs_GCJ" >&5
+echo "${ECHO_T}$ld_shlibs_GCJ" >&6
+test "$ld_shlibs_GCJ" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc_GCJ" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc_GCJ=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds_GCJ in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5
+echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6
+      $rm conftest*
+      printf "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+      if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } 2>conftest.err; then
+        soname=conftest
+        lib=conftest
+        libobjs=conftest.$ac_objext
+        deplibs=
+        wl=$lt_prog_compiler_wl_GCJ
+        compiler_flags=-v
+        linker_flags=-v
+        verstring=
+        output_objdir=.
+        libname=conftest
+        lt_save_allow_undefined_flag=$allow_undefined_flag_GCJ
+        allow_undefined_flag_GCJ=
+        if { (eval echo "$as_me:$LINENO: \"$archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5
+  (eval $archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+        then
+	  archive_cmds_need_lc_GCJ=no
+        else
+	  archive_cmds_need_lc_GCJ=yes
+        fi
+        allow_undefined_flag_GCJ=$lt_save_allow_undefined_flag
+      else
+        cat conftest.err 1>&5
+      fi
+      $rm conftest*
+      echo "$as_me:$LINENO: result: $archive_cmds_need_lc_GCJ" >&5
+echo "${ECHO_T}$archive_cmds_need_lc_GCJ" >&6
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5
+echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+if test "$GCC" = yes; then
+  sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+  if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+  else
+    sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+  fi
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix4* | aix5*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  library_names_spec='$libname.ixlibrary $libname.a'
+  # Create ${libname}_ixlibrary.a entries in /sys/libs.
+  finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi4*)
+  version_type=linux
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$host_os in
+  yes,cygwin* | yes,mingw* | yes,pw32*)
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $rm \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+      ;;
+    mingw*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+      if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH printed by
+        # mingw gcc, but we are running on Cygwin. Gcc prints its search
+        # path with ; separators, and with drive letters. We can handle the
+        # drive letters (cygwin fileutils understands them), so leave them,
+        # especially as we might pass files found there to a mingw objdump,
+        # which wouldn't understand a cygwinified path. Ahh.
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED  -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/./-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    ;;
+
+  *)
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    ;;
+  esac
+  dynamic_linker='Win32 ld.exe'
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='$(test .$module = .yes && echo .so || echo .dylib)'
+  # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same.
+  if test "$GCC" = yes; then
+    sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"`
+  else
+    sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib'
+  fi
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd1*)
+  dynamic_linker=no
+  ;;
+
+kfreebsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+freebsd*)
+  objformat=`test -x /usr/bin/objformat && /usr/bin/objformat || echo aout`
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.01* | freebsdelf3.01*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  *) # from 3.2 on
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case "$host_cpu" in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+   hppa*64*)
+     shrext_cmds='.sl'
+     hardcode_into_libs=yes
+     dynamic_linker="$host_os dld.sl"
+     shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+     shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+     library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+     soname_spec='${libname}${release}${shared_ext}$major'
+     sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+     sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+     ;;
+   *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555.
+  postinstall_cmds='chmod 555 $lib'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be Linux ELF.
+linux*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`$SED -e 's/:,\t/ /g;s/=^=*$//;s/=^= * / /g' /etc/ld.so.conf | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+knetbsd*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='GNU ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+nto-qnx*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+openbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=yes
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+sco3.2v5*)
+  version_type=osf
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+solaris*)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      export_dynamic_flag_spec='${wl}-Blargedynsym'
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+uts4*)
+  version_type=linux
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+echo "$as_me:$LINENO: result: $dynamic_linker" >&5
+echo "${ECHO_T}$dynamic_linker" >&6
+test "$dynamic_linker" = no && can_build_shared=no
+
+echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5
+echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6
+hardcode_action_GCJ=
+if test -n "$hardcode_libdir_flag_spec_GCJ" || \
+   test -n "$runpath_var GCJ" || \
+   test "X$hardcode_automatic_GCJ"="Xyes" ; then
+
+  # We can hardcode non-existant directories.
+  if test "$hardcode_direct_GCJ" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, GCJ)" != no &&
+     test "$hardcode_minus_L_GCJ" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action_GCJ=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action_GCJ=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action_GCJ=unsupported
+fi
+echo "$as_me:$LINENO: result: $hardcode_action_GCJ" >&5
+echo "${ECHO_T}$hardcode_action_GCJ" >&6
+
+if test "$hardcode_action_GCJ" = relink; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+striplib=
+old_striplib=
+echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5
+echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6
+if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+   darwin*)
+       if test -n "$STRIP" ; then
+         striplib="$STRIP -x"
+         echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+       else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+       ;;
+   *)
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+    ;;
+  esac
+fi
+
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+   ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+   ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+   ;;
+
+  *)
+    echo "$as_me:$LINENO: checking for shl_load" >&5
+echo $ECHO_N "checking for shl_load... $ECHO_C" >&6
+if test "${ac_cv_func_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define shl_load to an innocuous variant, in case <limits.h> declares shl_load.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define shl_load innocuous_shl_load
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char shl_load (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef shl_load
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_shl_load) || defined (__stub___shl_load)
+choke me
+#else
+char (*f) () = shl_load;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != shl_load;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5
+echo "${ECHO_T}$ac_cv_func_shl_load" >&6
+if test $ac_cv_func_shl_load = yes; then
+  lt_cv_dlopen="shl_load"
+else
+  echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5
+echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_shl_load+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+int
+main ()
+{
+shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_shl_load=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_shl_load=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6
+if test $ac_cv_lib_dld_shl_load = yes; then
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"
+else
+  echo "$as_me:$LINENO: checking for dlopen" >&5
+echo $ECHO_N "checking for dlopen... $ECHO_C" >&6
+if test "${ac_cv_func_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define dlopen to an innocuous variant, in case <limits.h> declares dlopen.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define dlopen innocuous_dlopen
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char dlopen (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef dlopen
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_dlopen) || defined (__stub___dlopen)
+choke me
+#else
+char (*f) () = dlopen;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != dlopen;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_func_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5
+echo "${ECHO_T}$ac_cv_func_dlopen" >&6
+if test $ac_cv_func_dlopen = yes; then
+  lt_cv_dlopen="dlopen"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5
+echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6
+if test "${ac_cv_lib_svld_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_svld_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_svld_dlopen=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6
+if test $ac_cv_lib_svld_dlopen = yes; then
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5
+echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6
+if test "${ac_cv_lib_dld_dld_link+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char dld_link ();
+int
+main ()
+{
+dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_dld_dld_link=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dld_dld_link=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5
+echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6
+if test $ac_cv_lib_dld_dld_link = yes; then
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5
+echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 18288 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self" >&6
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      LDFLAGS="$LDFLAGS $link_static_flag"
+      echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5
+echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6
+if test "${lt_cv_dlopen_self_static+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<EOF
+#line 18386 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef __cplusplus
+extern "C" void exit (int);
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+      /* dlclose (self); */
+    }
+
+    exit (status);
+}
+EOF
+  if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_unknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5
+echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    compiler_GCJ \
+    CC_GCJ \
+    LD_GCJ \
+    lt_prog_compiler_wl_GCJ \
+    lt_prog_compiler_pic_GCJ \
+    lt_prog_compiler_static_GCJ \
+    lt_prog_compiler_no_builtin_flag_GCJ \
+    export_dynamic_flag_spec_GCJ \
+    thread_safe_flag_spec_GCJ \
+    whole_archive_flag_spec_GCJ \
+    enable_shared_with_static_runtimes_GCJ \
+    old_archive_cmds_GCJ \
+    old_archive_from_new_cmds_GCJ \
+    predep_objects_GCJ \
+    postdep_objects_GCJ \
+    predeps_GCJ \
+    postdeps_GCJ \
+    compiler_lib_search_path_GCJ \
+    archive_cmds_GCJ \
+    archive_expsym_cmds_GCJ \
+    postinstall_cmds_GCJ \
+    postuninstall_cmds_GCJ \
+    old_archive_from_expsyms_cmds_GCJ \
+    allow_undefined_flag_GCJ \
+    no_undefined_flag_GCJ \
+    export_symbols_cmds_GCJ \
+    hardcode_libdir_flag_spec_GCJ \
+    hardcode_libdir_flag_spec_ld_GCJ \
+    hardcode_libdir_separator_GCJ \
+    hardcode_automatic_GCJ \
+    module_cmds_GCJ \
+    module_expsym_cmds_GCJ \
+    lt_cv_prog_compiler_c_o_GCJ \
+    exclude_expsyms_GCJ \
+    include_expsyms_GCJ; do
+
+    case $var in
+    old_archive_cmds_GCJ | \
+    old_archive_from_new_cmds_GCJ | \
+    archive_cmds_GCJ | \
+    archive_expsym_cmds_GCJ | \
+    module_cmds_GCJ | \
+    module_expsym_cmds_GCJ | \
+    old_archive_from_expsyms_cmds_GCJ | \
+    export_symbols_cmds_GCJ | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\$0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+cfgfile="$ofile"
+
+  cat <<__EOF__ >> "$cfgfile"
+# ### BEGIN LIBTOOL TAG CONFIG: $tagname
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_GCJ
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_GCJ
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_compiler_GCJ
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC_GCJ
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_LD_GCJ
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_GCJ
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_GCJ
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_GCJ
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_GCJ
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_GCJ
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_GCJ
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_GCJ
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec_GCJ
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds_GCJ
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_GCJ
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_GCJ
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds_GCJ
+archive_expsym_cmds=$lt_archive_expsym_cmds_GCJ
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_module_cmds_GCJ
+module_expsym_cmds=$lt_module_expsym_cmds_GCJ
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_predep_objects_GCJ
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_postdep_objects_GCJ
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_predeps_GCJ
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_postdeps_GCJ
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_GCJ
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_GCJ
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_GCJ
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_GCJ
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_GCJ
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_GCJ
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_GCJ
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct_GCJ
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L_GCJ
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_GCJ
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$hardcode_automatic_GCJ
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_GCJ
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path_GCJ"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols_GCJ
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_GCJ
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_GCJ
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_GCJ
+
+# ### END LIBTOOL TAG CONFIG: $tagname
+
+__EOF__
+
+
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+	else
+	  tagname=""
+	fi
+	;;
+
+      RC)
+
+
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+objext_RC=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+CC=${RC-"windres"}
+compiler=$CC
+compiler_RC=$CC
+lt_cv_prog_compiler_c_o_RC=yes
+
+# The else clause should only fire when bootstrapping the
+# libtool distribution, otherwise you forgot to ship ltmain.sh
+# with your package, and you will get complaints that there are
+# no rules to generate ltmain.sh.
+if test -f "$ltmain"; then
+  # See if we are running on zsh, and set the options which allow our commands through
+  # without removal of \ escapes.
+  if test -n "${ZSH_VERSION+set}" ; then
+    setopt NO_GLOB_SUBST
+  fi
+  # Now quote all the things that may contain metacharacters while being
+  # careful not to overquote the AC_SUBSTed values.  We take copies of the
+  # variables and quote the copies for generation of the libtool script.
+  for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC NM \
+    SED SHELL STRIP \
+    libname_spec library_names_spec soname_spec extract_expsyms_cmds \
+    old_striplib striplib file_magic_cmd finish_cmds finish_eval \
+    deplibs_check_method reload_flag reload_cmds need_locks \
+    lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \
+    lt_cv_sys_global_symbol_to_c_name_address \
+    sys_lib_search_path_spec sys_lib_dlsearch_path_spec \
+    old_postinstall_cmds old_postuninstall_cmds \
+    compiler_RC \
+    CC_RC \
+    LD_RC \
+    lt_prog_compiler_wl_RC \
+    lt_prog_compiler_pic_RC \
+    lt_prog_compiler_static_RC \
+    lt_prog_compiler_no_builtin_flag_RC \
+    export_dynamic_flag_spec_RC \
+    thread_safe_flag_spec_RC \
+    whole_archive_flag_spec_RC \
+    enable_shared_with_static_runtimes_RC \
+    old_archive_cmds_RC \
+    old_archive_from_new_cmds_RC \
+    predep_objects_RC \
+    postdep_objects_RC \
+    predeps_RC \
+    postdeps_RC \
+    compiler_lib_search_path_RC \
+    archive_cmds_RC \
+    archive_expsym_cmds_RC \
+    postinstall_cmds_RC \
+    postuninstall_cmds_RC \
+    old_archive_from_expsyms_cmds_RC \
+    allow_undefined_flag_RC \
+    no_undefined_flag_RC \
+    export_symbols_cmds_RC \
+    hardcode_libdir_flag_spec_RC \
+    hardcode_libdir_flag_spec_ld_RC \
+    hardcode_libdir_separator_RC \
+    hardcode_automatic_RC \
+    module_cmds_RC \
+    module_expsym_cmds_RC \
+    lt_cv_prog_compiler_c_o_RC \
+    exclude_expsyms_RC \
+    include_expsyms_RC; do
+
+    case $var in
+    old_archive_cmds_RC | \
+    old_archive_from_new_cmds_RC | \
+    archive_cmds_RC | \
+    archive_expsym_cmds_RC | \
+    module_cmds_RC | \
+    module_expsym_cmds_RC | \
+    old_archive_from_expsyms_cmds_RC | \
+    export_symbols_cmds_RC | \
+    extract_expsyms_cmds | reload_cmds | finish_cmds | \
+    postinstall_cmds | postuninstall_cmds | \
+    old_postinstall_cmds | old_postuninstall_cmds | \
+    sys_lib_search_path_spec | sys_lib_dlsearch_path_spec)
+      # Double-quote double-evaled strings.
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+      ;;
+    *)
+      eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\""
+      ;;
+    esac
+  done
+
+  case $lt_echo in
+  *'\$0 --fallback-echo"')
+    lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'`
+    ;;
+  esac
+
+cfgfile="$ofile"
+
+  cat <<__EOF__ >> "$cfgfile"
+# ### BEGIN LIBTOOL TAG CONFIG: $tagname
+
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_RC
+
+# Whether or not to disallow shared libs when runtime libs are static
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_RC
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+
+# An echo program that does not interpret backslashes.
+echo=$lt_echo
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A C compiler.
+LTCC=$lt_LTCC
+
+# A language-specific compiler.
+CC=$lt_compiler_RC
+
+# Is the compiler the GNU C compiler?
+with_gcc=$GCC_RC
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# The linker used to build libraries.
+LD=$lt_LD_RC
+
+# Whether we need hard or soft links.
+LN_S=$lt_LN_S
+
+# A BSD-compatible nm program.
+NM=$lt_NM
+
+# A symbol stripping program
+STRIP=$lt_STRIP
+
+# Used to examine libraries when file_magic_cmd begins "file"
+MAGIC_CMD=$MAGIC_CMD
+
+# Used on cygwin: DLL creation program.
+DLLTOOL="$DLLTOOL"
+
+# Used on cygwin: object dumper.
+OBJDUMP="$OBJDUMP"
+
+# Used on cygwin: assembler.
+AS="$AS"
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_RC
+
+# Object file suffix (normally "o").
+objext="$ac_objext"
+
+# Old archive suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally ".so").
+shrext_cmds='$shrext_cmds'
+
+# Executable file suffix (normally "").
+exeext="$exeext"
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_RC
+pic_mode=$pic_mode
+
+# What is the maximum length of a command?
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_RC
+
+# Must we lock files when doing compilation ?
+need_locks=$lt_need_locks
+
+# Do we need the lib prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_RC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_RC
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_RC
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_RC
+
+# Compiler flag to generate thread-safe objects.
+thread_safe_flag_spec=$lt_thread_safe_flag_spec_RC
+
+# Library versioning type.
+version_type=$version_type
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME.
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Commands used to build and install an old-style archive.
+RANLIB=$lt_RANLIB
+old_archive_cmds=$lt_old_archive_cmds_RC
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_RC
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_RC
+
+# Commands used to build and install a shared archive.
+archive_cmds=$lt_archive_cmds_RC
+archive_expsym_cmds=$lt_archive_expsym_cmds_RC
+postinstall_cmds=$lt_postinstall_cmds
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to build a loadable module (assumed same as above if empty)
+module_cmds=$lt_module_cmds_RC
+module_expsym_cmds=$lt_module_expsym_cmds_RC
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predep_objects=$lt_predep_objects_RC
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdep_objects=$lt_postdep_objects_RC
+
+# Dependencies to place before the objects being linked to create a
+# shared library.
+predeps=$lt_predeps_RC
+
+# Dependencies to place after the objects being linked to create a
+# shared library.
+postdeps=$lt_postdeps_RC
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_RC
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == file_magic.
+file_magic_cmd=$lt_file_magic_cmd
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_RC
+
+# Flag that forces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_RC
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# Same as above, but a single script fragment to be evaled but not shown.
+finish_eval=$lt_finish_eval
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# This is the shared library runtime path variable.
+runpath_var=$runpath_var
+
+# This is the shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_RC
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_RC
+
+# If ld is used when linking, flag to hardcode \$libdir into
+# a binary during linking. This must work even if \$libdir does
+# not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_RC
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_RC
+
+# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct=$hardcode_direct_RC
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L=$hardcode_minus_L_RC
+
+# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into
+# the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_RC
+
+# Set to yes if building a shared library automatically hardcodes DIR into the library
+# and all subsequent libraries and executables linked against it.
+hardcode_automatic=$hardcode_automatic_RC
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at relink time.
+variables_saved_for_relink="$variables_saved_for_relink"
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_RC
+
+# Compile-time system search path for libraries
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path="$fix_srcfile_path_RC"
+
+# Set to yes if exported symbols are required.
+always_export_symbols=$always_export_symbols_RC
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_RC
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_RC
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_RC
+
+# ### END LIBTOOL TAG CONFIG: $tagname
+
+__EOF__
+
+
+else
+  # If there is no Makefile yet, we rely on a make rule to execute
+  # `config.status --recheck' to rerun these tests and create the
+  # libtool script then.
+  ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'`
+  if test -f "$ltmain_in"; then
+    test -f Makefile && make "$ltmain"
+  fi
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+	;;
+
+      *)
+	{ { echo "$as_me:$LINENO: error: Unsupported tag name: $tagname" >&5
+echo "$as_me: error: Unsupported tag name: $tagname" >&2;}
+   { (exit 1); exit 1; }; }
+	;;
+      esac
+
+      # Append the new tag name to the list of available tags.
+      if test -n "$tagname" ; then
+      available_tags="$available_tags $tagname"
+    fi
+    fi
+  done
+  IFS="$lt_save_ifs"
+
+  # Now substitute the updated list of available tags.
+  if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then
+    mv "${ofile}T" "$ofile"
+    chmod +x "$ofile"
+  else
+    rm -f "${ofile}T"
+    { { echo "$as_me:$LINENO: error: unable to update list of available tagged configurations." >&5
+echo "$as_me: error: unable to update list of available tagged configurations." >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+fi
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+# Prevent multiple expansion
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in linux/netfilter_bridge/ebtables.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+                    #include <sys/socket.h>
+                    #include <netinet/in.h>
+                    #include <linux/netfilter_bridge/ebtables.h>
+
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+   { { echo "$as_me:$LINENO: error:
+
+ERROR: kernel header file 'linux/netfilter_bridge/ebtables.h' is required.
+
+Workaround
+
+  - Install kernel headers.
+
+" >&5
+echo "$as_me: error:
+
+ERROR: kernel header file 'linux/netfilter_bridge/ebtables.h' is required.
+
+Workaround
+
+  - Install kernel headers.
+
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in malloc free memset memcpy strcmp snprintf socket
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+for ac_func in getsockopt setsockopt close
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+
+
+          ac_config_headers="$ac_config_headers include/config.h"
+
+
+                                        ac_config_files="$ac_config_files Makefile src/Makefile include/Makefile test/Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+	"s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[	 ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[	 ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+	 sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)$' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+  	  /^X\/\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\/\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+	 case $as_dir in
+	 /*)
+	   if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+	     $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+	     $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+	     CONFIG_SHELL=$as_dir/$as_base
+	     export CONFIG_SHELL
+	     exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+	   fi;;
+	 esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" 	$as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by EBTC $as_me 0.1.0, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+		   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+		   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+EBTC config.status 0.1.0
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+#
+# INIT-COMMANDS section.
+#
+
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+_ACEOF
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+  "include/Makefile" ) CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
+  "test/Makefile" ) CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
+  "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+  "include/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@CYGPATH_W@,$CYGPATH_W,;t t
+s,@PACKAGE@,$PACKAGE,;t t
+s,@VERSION@,$VERSION,;t t
+s,@ACLOCAL@,$ACLOCAL,;t t
+s,@AUTOCONF@,$AUTOCONF,;t t
+s,@AUTOMAKE@,$AUTOMAKE,;t t
+s,@AUTOHEADER@,$AUTOHEADER,;t t
+s,@MAKEINFO@,$MAKEINFO,;t t
+s,@install_sh@,$install_sh,;t t
+s,@STRIP@,$STRIP,;t t
+s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
+s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t
+s,@mkdir_p@,$mkdir_p,;t t
+s,@AWK@,$AWK,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@am__leading_dot@,$am__leading_dot,;t t
+s,@AMTAR@,$AMTAR,;t t
+s,@am__tar@,$am__tar,;t t
+s,@am__untar@,$am__untar,;t t
+s,@EBTC_LT_CURRENT@,$EBTC_LT_CURRENT,;t t
+s,@EBTC_LT_REVISION@,$EBTC_LT_REVISION,;t t
+s,@EBTC_LT_AGE@,$EBTC_LT_AGE,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@DEPDIR@,$DEPDIR,;t t
+s,@am__include@,$am__include,;t t
+s,@am__quote@,$am__quote,;t t
+s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t
+s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t
+s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t
+s,@CCDEPMODE@,$CCDEPMODE,;t t
+s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
+s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
+s,@CPP@,$CPP,;t t
+s,@LN_S@,$LN_S,;t t
+s,@EGREP@,$EGREP,;t t
+s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t
+s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t
+s,@MAINT@,$MAINT,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@ECHO@,$ECHO,;t t
+s,@AR@,$AR,;t t
+s,@ac_ct_AR@,$ac_ct_AR,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@CXX@,$CXX,;t t
+s,@CXXFLAGS@,$CXXFLAGS,;t t
+s,@ac_ct_CXX@,$ac_ct_CXX,;t t
+s,@CXXDEPMODE@,$CXXDEPMODE,;t t
+s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t
+s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t
+s,@CXXCPP@,$CXXCPP,;t t
+s,@F77@,$F77,;t t
+s,@FFLAGS@,$FFLAGS,;t t
+s,@ac_ct_F77@,$ac_ct_F77,;t t
+s,@LIBTOOL@,$LIBTOOL,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+				     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([	 ]*\)#\([	 ]*define[	 ][	 ]*\)'
+ac_dB='[	 ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([	 ]*\)#\([	 ]*\)undef\([	 ][	 ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+	cat >$tmp/stdin
+	ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+	ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+	 # Absolute (can't be DOS-style, as IFS=:)
+	 test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 # Do quote $f, to prevent DOS paths from being IFS'd.
+	 echo "$f";;
+      *) # Relative
+	 if test -f "$f"; then
+	   # Build tree
+	   echo "$f"
+	 elif test -f "$srcdir/$f"; then
+	   # Source tree
+	   echo "$srcdir/$f"
+	 else
+	   # /dev/null tree
+	   { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+	 fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[	 ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*\)\(([^)]*)\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[	 ]*#[	 ]*undef[	 ][	 ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[	 ]*#[	 ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[	 ]*#[	 ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[	 ]*#[	 ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+# Compute $ac_file's index in $config_headers.
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $ac_file | $ac_file:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null ||
+$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X$ac_file : 'X\(//\)[^/]' \| \
+	 X$ac_file : 'X\(//\)$' \| \
+	 X$ac_file : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X$ac_file |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`/stamp-h$_am_stamp_count
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_COMMANDS section.
+#
+for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
+  ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
+  ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
+  ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
+$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_dest" : 'X\(//\)[^/]' \| \
+	 X"$ac_dest" : 'X\(//\)$' \| \
+	 X"$ac_dest" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_dest" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
+echo "$as_me: executing $ac_dest commands" >&6;}
+  case $ac_dest in
+    depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
+  # Strip MF so we end up with the name of the file.
+  mf=`echo "$mf" | sed -e 's/:.*$//'`
+  # Check whether this is an Automake generated Makefile or not.
+  # We used to match only the files named `Makefile.in', but
+  # some people rename them; so instead we look at the file content.
+  # Grep'ing the first line is not enough: some people post-process
+  # each Makefile.in and add a new line on top of each file to say so.
+  # So let's grep whole file.
+  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
+    dirpart=`(dirname "$mf") 2>/dev/null ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$mf" : 'X\(//\)[^/]' \| \
+	 X"$mf" : 'X\(//\)$' \| \
+	 X"$mf" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+  else
+    continue
+  fi
+  # Extract the definition of DEPDIR, am__include, and am__quote
+  # from the Makefile without running `make'.
+  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  am__include=`sed -n 's/^am__include = //p' < "$mf"`
+  test -z "am__include" && continue
+  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n 's/^U = //p' < "$mf"`
+  # Find all dependency output files, they are included files with
+  # $(DEPDIR) in their names.  We invoke sed twice because it is the
+  # simplest approach to changing $(DEPDIR) to its actual value in the
+  # expansion.
+  for file in `sed -n "
+    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`(dirname "$file") 2>/dev/null ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$file" : 'X\(//\)[^/]' \| \
+	 X"$file" : 'X\(//\)$' \| \
+	 X"$file" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    { if $as_mkdir_p; then
+    mkdir -p $dirpart/$fdir
+  else
+    as_dir=$dirpart/$fdir
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| \
+	 .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+  	  /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+  	  /^X\(\/\/\)$/{ s//\1/; q; }
+  	  /^X\(\/\).*/{ s//\1/; q; }
+  	  s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5
+echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+ ;;
+  esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+
+
diff --git a/userspace/libebtc/configure.in b/userspace/libebtc/configure.in
new file mode 100644
index 0000000..e4244a5
--- /dev/null
+++ b/userspace/libebtc/configure.in
@@ -0,0 +1,94 @@
+
+
+AC_PREREQ(2.53)
+
+
+dnl
+dnl Version settings
+dnl
+
+AC_INIT(EBTC, 0.1.0, libebtc@bugreport.1in1.de)
+AM_INIT_AUTOMAKE(EBTC, 0.1.0)
+
+EBTC_LT_CURRENT=1
+EBTC_LT_REVISION=0
+EBTC_LT_AGE=1
+
+AC_SUBST(EBTC_LT_CURRENT)
+AC_SUBST(EBTC_LT_REVISION)
+AC_SUBST(EBTC_LT_AGE)
+
+
+
+dnl
+dnl Arguments
+dnl
+
+
+
+dnl
+dnl General checks
+dnl
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_C_BIGENDIAN
+AC_STDC_HEADERS
+
+
+
+dnl
+dnl Options
+dnl
+
+AM_MAINTAINER_MODE
+AM_PROG_LIBTOOL
+
+
+dnl
+dnl Check headers
+dnl
+
+AC_CHECK_HEADERS(linux/netfilter_bridge/ebtables.h,
+                 [],
+                 [ AC_MSG_ERROR([
+
+ERROR: kernel header file 'linux/netfilter_bridge/ebtables.h' is required.
+
+Workaround
+
+  - Install kernel headers.
+
+]) ],
+                 [
+                    #include <sys/socket.h>
+                    #include <netinet/in.h>
+                    #include <linux/netfilter_bridge/ebtables.h>
+                 ])
+
+
+
+dnl
+dnl Check functions
+dnl
+
+AC_CHECK_FUNCS(malloc free memset memcpy strcmp snprintf socket)
+AC_CHECK_FUNCS(getsockopt setsockopt close)
+
+
+
+dnl
+dnl Output
+dnl
+
+AC_CONFIG_HEADER(include/config.h)
+
+AC_OUTPUT(Makefile
+          src/Makefile
+          include/Makefile
+          test/Makefile)
+
+
+
diff --git a/userspace/libebtc/depcomp b/userspace/libebtc/depcomp
new file mode 100755
index 0000000..11e2d3b
--- /dev/null
+++ b/userspace/libebtc/depcomp
@@ -0,0 +1,522 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2004-05-31.23
+
+# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by `PROGRAMS ARGS'.
+  object      Object file output by `PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputing dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit 0
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit 0
+    ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+  "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'.  On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like `#:fec' to the end of the
+    # dependency line.
+    tr ' ' '
+' < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr '
+' ' ' >> $depfile
+    echo >> $depfile
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' '
+' < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> $depfile
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
+  tmpdepfile="$stripped.u"
+  if test "$libtool" = yes; then
+    "$@" -Wc,-M
+  else
+    "$@" -M
+  fi
+  stat=$?
+
+  if test -f "$tmpdepfile"; then :
+  else
+    stripped=`echo "$stripped" | sed 's,^.*/,,'`
+    tmpdepfile="$stripped.u"
+  fi
+
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+
+  if test -f "$tmpdepfile"; then
+    outname="$stripped.o"
+    # Each line is of the form `foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # `$object: dependent.h' and one to simply `dependent.h:'.
+    sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+    sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+icc)
+  # Intel's C compiler understands `-MD -MF file'.  However on
+  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # ICC 7.0 will fill foo.d with something like
+  #    foo.o: sub/foo.c
+  #    foo.o: sub/foo.h
+  # which is wrong.  We want:
+  #    sub/foo.o: sub/foo.c
+  #    sub/foo.o: sub/foo.h
+  #    sub/foo.c:
+  #    sub/foo.h:
+  # ICC 7.1 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using \ :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+    sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+tru64)
+   # The Tru64 compiler uses -MD to generate dependencies as a side
+   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+   # dependencies in `foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+   test "x$dir" = "x$object" && dir=
+   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+   if test "$libtool" = yes; then
+      # Dependencies are output in .lo.d with libtool 1.4.
+      # With libtool 1.5 they are output both in $dir.libs/$base.o.d
+      # and in $dir.libs/$base.o.d and $dir$base.o.d.  We process the
+      # latter, because the former will be cleaned when $dir.libs is
+      # erased.
+      tmpdepfile1="$dir.libs/$base.lo.d"
+      tmpdepfile2="$dir$base.o.d"
+      tmpdepfile3="$dir.libs/$base.d"
+      "$@" -Wc,-MD
+   else
+      tmpdepfile1="$dir$base.o.d"
+      tmpdepfile2="$dir$base.d"
+      tmpdepfile3="$dir$base.d"
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+      exit $stat
+   fi
+
+   if test -f "$tmpdepfile1"; then
+      tmpdepfile="$tmpdepfile1"
+   elif test -f "$tmpdepfile2"; then
+      tmpdepfile="$tmpdepfile2"
+   else
+      tmpdepfile="$tmpdepfile3"
+   fi
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      # That's a tab and a space in the [].
+      sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for `:'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  "$@" $dashmflag |
+    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no
+  for arg in "$@"; do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix="`echo $object | sed 's/^.*\././'`"
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E |
+    sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o,
+  # because we must use -o when running libtool.
+  "$@" || exit $?
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+	set fnord "$@"
+	shift
+	shift
+	;;
+    *)
+	set fnord "$@" "$arg"
+	shift
+	shift
+	;;
+    esac
+  done
+  "$@" -E |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::	\1 \\:p' >> "$depfile"
+  echo "	" >> "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/userspace/libebtc/include/Makefile.am b/userspace/libebtc/include/Makefile.am
new file mode 100644
index 0000000..1a9972c
--- /dev/null
+++ b/userspace/libebtc/include/Makefile.am
@@ -0,0 +1,43 @@
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+
+MAINTAINERCLEANFILES        = Makefile.in
+
+include_HEADERS             = libebtc.h
+
+
diff --git a/userspace/libebtc/include/Makefile.in b/userspace/libebtc/include/Makefile.in
new file mode 100644
index 0000000..4d65b8a
--- /dev/null
+++ b/userspace/libebtc/include/Makefile.in
@@ -0,0 +1,450 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = include
+DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in $(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(includedir)"
+includeHEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+EBTC_LT_AGE = @EBTC_LT_AGE@
+EBTC_LT_CURRENT = @EBTC_LT_CURRENT@
+EBTC_LT_REVISION = @EBTC_LT_REVISION@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+MAINTAINERCLEANFILES = Makefile.in
+include_HEADERS = libebtc.h
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  include/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  include/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+config.h: stamp-h1
+	@if test ! -f $@; then \
+	  rm -f stamp-h1; \
+	  $(MAKE) stamp-h1; \
+	else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+	cd $(top_srcdir) && $(AUTOHEADER)
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+install-includeHEADERS: $(include_HEADERS)
+	@$(NORMAL_INSTALL)
+	test -z "$(includedir)" || $(mkdir_p) "$(DESTDIR)$(includedir)"
+	@list='$(include_HEADERS)'; for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  f=$(am__strip_dir) \
+	  echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+	  $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+	done
+
+uninstall-includeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(include_HEADERS)'; for p in $$list; do \
+	  f=$(am__strip_dir) \
+	  echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+	  rm -f "$(DESTDIR)$(includedir)/$$f"; \
+	done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    dir="/$$dir"; \
+	    $(mkdir_p) "$(distdir)$$dir"; \
+	  else \
+	    dir=''; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+	for dir in "$(DESTDIR)$(includedir)"; do \
+	  test -z "$$dir" || $(mkdir_p) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr \
+	distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool ctags distclean distclean-generic distclean-hdr \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-exec install-exec-am \
+	install-includeHEADERS install-info install-info-am \
+	install-man install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags uninstall uninstall-am uninstall-includeHEADERS \
+	uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/userspace/libebtc/include/config.h.in b/userspace/libebtc/include/config.h.in
new file mode 100644
index 0000000..6bb299b
--- /dev/null
+++ b/userspace/libebtc/include/config.h.in
@@ -0,0 +1,93 @@
+/* include/config.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the `close' function. */
+#undef HAVE_CLOSE
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `free' function. */
+#undef HAVE_FREE
+
+/* Define to 1 if you have the `getsockopt' function. */
+#undef HAVE_GETSOCKOPT
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <linux/netfilter_bridge/ebtables.h> header
+   file. */
+#undef HAVE_LINUX_NETFILTER_BRIDGE_EBTABLES_H
+
+/* Define to 1 if you have the `malloc' function. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the `memcpy' function. */
+#undef HAVE_MEMCPY
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `setsockopt' function. */
+#undef HAVE_SETSOCKOPT
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcmp' function. */
+#undef HAVE_STRCMP
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
diff --git a/userspace/libebtc/include/libebtc.h b/userspace/libebtc/include/libebtc.h
new file mode 100644
index 0000000..7c12b14
--- /dev/null
+++ b/userspace/libebtc/include/libebtc.h
@@ -0,0 +1,838 @@
+/*
+ * ==[ FILENAME: libebtc.h ]====================================================
+ *
+ *  Project
+ *
+ *      Library for ethernet bridge tables.
+ *
+ *
+ *  Description
+ *
+ *      See project.
+ *
+ *
+ *  Copyright
+ *
+ *      Copyright 2005 by Jens Götze
+ *      All rights reserved.
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+ *      USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#ifndef __LIB_EBTC_H__
+#define __LIB_EBTC_H__ 1
+
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+
+
+/* Macros */
+
+#ifndef EBTC_MIN_ALIGN
+#  define EBTC_MIN_ALIGN                (__alignof__(struct ebt_entry_target))
+#endif
+#define EBTC_ALIGN(s)                   (((s) + (EBTC_MIN_ALIGN - 1)) & \
+                                         ~(EBTC_MIN_ALIGN - 1))
+
+#define EBTC_SIZEOF(a)                  EBTC_ALIGN(sizeof(a))
+#define EBTC_NEXT(a)                    ((char *)(a) + EBTC_ALIGN(sizeof(*(a))))
+#define EBTC_ADDOFFSET(a, b)            ((char *)(a) + (b))
+
+#define EBTC_INIT                       0x0000
+#define EBTC_INIT_WITHFLUSH             0x0001
+
+#define EBTC_FALSE                      -1
+#define EBTC_TRUE                       0
+
+
+/* Types */
+
+typedef struct ebtc_handle_st *ebtc_handle_t;
+
+typedef struct ebt_replace ebt_replace_t;
+
+typedef struct ebt_entries ebt_entries_t;
+
+typedef struct ebt_entry ebt_entry_t;
+
+typedef struct ebt_counter ebt_counter_t;
+
+typedef struct ebt_entry_target ebt_entry_target_t;
+
+typedef struct ebt_standard_target ebt_standard_target_t;
+
+
+/* Functions */
+
+/*
+ * ==[ FUNCTION: ebtc_is_chain ]================================================
+ *
+ *  Description
+ *
+ *      ebtc_is_chain checks chainname for exist.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_is_chain (const char *chainname, const ebtc_handle_t handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_first_chain ]=============================================
+ *
+ *  Description
+ *
+ *      ebtc_first_chain returns the first chain.
+ *
+ *
+ *  Parameter
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if it end of list, or non-zero for pointer of chainname.
+ *
+ *
+ * =============================================================================
+ */
+extern const char *ebtc_first_chain (ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_next_chain ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_next_chain returns the next chain.
+ *
+ *
+ *  Parameter
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if it end of list, or non-zero for pointer of chainname.
+ *
+ *
+ * =============================================================================
+ */
+extern const char *ebtc_next_chain (ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_first_rule ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_first_rule returns the first rule in chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if it end of list, or non-zero for pointer of entry.
+ *
+ *
+ * =============================================================================
+ */
+extern const struct ebt_entry *ebtc_first_rule (const char *chainname,
+                                                ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_next_rule ]===============================================
+ *
+ *  Description
+ *
+ *      ebtc_next_rule returns the next rule in chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if it end of list, or non-zero for pointer of entry.
+ *
+ *
+ * =============================================================================
+ */
+extern const struct ebt_entry *ebtc_next_rule (const char *chainname,
+                                               ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_get_target ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_get_target returns target of an rule.
+ *
+ *
+ *  Parameter
+ *
+ *      I   entry
+ *              Pointer to an ebt_entry object with target.
+ *
+ *      I   handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if it end of list, or non-zero for pointer of entry.
+ *
+ *
+ * =============================================================================
+ */
+extern const char *ebtc_get_target (const struct ebt_entry *entry,
+                                    const ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_is_builtin ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_is_builtin checks a chainname for built-in chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it's a built-in chain, or non-zero for a non built-in
+ *      chain.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_is_builtin (const char *chainname, const ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_set_policy ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_set_policy set policy of a chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   policy
+ *              C-String with policy (ACCEPT, DROP, CONTINUE or RETURN).
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_set_policy (const char *chainname, const char *policy,
+                            ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_get_policy ]==============================================
+ *
+ *  Description
+ *
+ *      ebtc_get_policy get policy of a chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns a c-ctring with policy (ACCEPT, DROP, CONTINUE or RETURN).
+ *
+ *
+ * =============================================================================
+ */
+extern const char *ebtc_get_policy (const char *chainname,
+                                    const ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_insert_entry ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_insert_entry insert a entry. You find details for the entry-object
+ *      in linux/netfilter_bridge/ebtables.h.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   entry
+ *              Pointer for a entry-object.
+ *
+ *      I   rulenum
+ *              Position of new rule.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_insert_entry (const char *chainname,
+                              const struct ebt_entry *entry,
+                              unsigned int rulenum, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_replace_entry ]===========================================
+ *
+ *  Description
+ *
+ *      ebtc_replace_entry replace a entry. You find details for the entry-object
+ *      in linux/netfilter_bridge/ebtables.h.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   entry
+ *              Pointer for the new entry-object.
+ *
+ *      I   rulenum
+ *              Position of new rule.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_replace_entry (const char *chainname,
+                               const struct ebt_entry *entry,
+                               unsigned int rulenum, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_append_entry ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_append_entry append a entry. You find details for the entry-object
+ *      in linux/netfilter_bridge/ebtables.h.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   entry
+ *              Pointer for a entry-object.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_append_entry (const char *chainname,
+                              const struct ebt_entry *entry,
+                              ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_delete_entry ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_append_entry delete a entry.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   rulenum
+ *              Position of rule.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_delete_entry (const char *chainname, unsigned int rulenum,
+                              ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_target_jumptochain ]======================================
+ *
+ *  Description
+ *
+ *      ebtc_target_jumptochain initialize standard target for jump to a chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_target_jumptochain (ebt_standard_target_t *target,
+                                    char *chainname, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_flush_entries ]===========================================
+ *
+ *  Description
+ *
+ *      ebtc_flush_entries flush all entries in a chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_flush_entries (const char *chainname, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_zero_entries ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_zero_entries set all counter in a chain to zero.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_zero_entries (const char *chainname, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_rename_chain ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_rename_chain rename a chain. A built-in chain returns an error.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname_old
+ *              C-String with chainname.
+ *
+ *      I   chainname_new
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_rename_chain (const char *chainname_old,
+                              const char *chainname_new, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_create_chain ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_create_chain create a chain.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_create_chain (const char *chainname, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_delete_chain ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_delete_chain delete a chain. A built-in chain returns a error.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_delete_chain (const char *chainname, ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_read_counter ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_read_counter returns the current counter for packet and bytes.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with name of chain.
+ *
+ *      I   rulenum
+ *              Position of rule.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL for an error, or non-zero for the counter.
+ *
+ *
+ * =============================================================================
+ */
+extern const struct ebt_counter *ebtc_read_counter (const char *chainname, 
+                                                    unsigned int rulenum,
+                                                    ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_zero_counter ]============================================
+ *
+ *  Description
+ *
+ *      ebtc_zero_counter set counter of a rule to zero.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   rulenum
+ *              Postion of rule.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_zero_counter (const char *chainname, unsigned int rulenum,
+                              ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_set_counter ]=============================================
+ *
+ *  Description
+ *
+ *      ebtc_set_counter set counter to a specific value.
+ *
+ *
+ *  Parameter
+ *
+ *      I   chainname
+ *              C-String with chainname.
+ *
+ *      I   rulenum
+ *              Postion of rule.
+ *
+ *      I   counters
+ *              New counter value.
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_set_counter (const char *chainname, unsigned int rulenum,
+                             const struct ebt_counter *counters,
+                             ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_init ]====================================================
+ *
+ *  Description
+ *
+ *      ebtc_init create a new handle for the tablename.
+ *
+ *
+ *  Parameter
+ *
+ *      I   tablename
+ *              C-String with name of table.
+ *
+ *      I   options
+ *              Options, look at macros with the prefix "EBTC_INIT".
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL for an error, or non-zero for the handle.
+ *
+ *
+ * =============================================================================
+ */
+extern ebtc_handle_t ebtc_init (const char *tablename, int options);
+
+
+/*
+ * ==[ FUNCTION: ebtc_commit ]==================================================
+ *
+ *  Description
+ *
+ *      ebtc_commit submit all changes to kernel. After the submit, handle is
+ *      freed.
+ *
+ *
+ *  Parameter
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns 0 if it succeeds, or non-zero for an error.
+ *
+ *
+ * =============================================================================
+ */
+extern int ebtc_commit (ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_free ]====================================================
+ *
+ *  Description
+ *
+ *      ebtc_free free a handle.
+ *
+ *
+ *  Parameter
+ *
+ *      IO  handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns nothing.
+ *
+ *
+ * =============================================================================
+ */
+extern void ebtc_free (ebtc_handle_t *handle);
+
+
+/*
+ * ==[ FUNCTION: ebtc_strerror ]================================================
+ *
+ *  Description
+ *
+ *      ebtc_strerror translate error id to a human readable message. If no
+ *      handle exist (ebtc_init or ebtc_free return an error), use NULL. With
+ *      option used ebtc_strerror an global variable. hint, in a
+ *      multi-threading environment can return this method a wrong message.
+ *
+ *
+ *  Parameter
+ *
+ *      I   handle
+ *              EBTables handle
+ *
+ *
+ *  Return value
+ *
+ *      Returns NULL if message not exist, or non-zero for the message.
+ *
+ *
+ * =============================================================================
+ */
+extern const char *ebtc_strerror (const ebtc_handle_t *handle);
+
+
+#endif
+
+
diff --git a/userspace/libebtc/install-sh b/userspace/libebtc/install-sh
new file mode 100755
index 0000000..0b65ee8
--- /dev/null
+++ b/userspace/libebtc/install-sh
@@ -0,0 +1,323 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2004-10-22.00
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+chmodcmd="$chmodprog 0755"
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c         (ignored)
+-d         create directories instead of installing files.
+-g GROUP   $chgrpprog installed files to GROUP.
+-m MODE    $chmodprog installed files to MODE.
+-o USER    $chownprog installed files to USER.
+-s         $stripprog installed files.
+-t DIRECTORY  install into DIRECTORY.
+-T         report an error if DSTFILE is a directory.
+--help     display this help and exit.
+--version  display version info and exit.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test -n "$1"; do
+  case $1 in
+    -c) shift
+        continue;;
+
+    -d) dir_arg=true
+        shift
+        continue;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift
+        shift
+        continue;;
+
+    --help) echo "$usage"; exit 0;;
+
+    -m) chmodcmd="$chmodprog $2"
+        shift
+        shift
+        continue;;
+
+    -o) chowncmd="$chownprog $2"
+        shift
+        shift
+        continue;;
+
+    -s) stripcmd=$stripprog
+        shift
+        continue;;
+
+    -t) dstarg=$2
+	shift
+	shift
+	continue;;
+
+    -T) no_target_directory=true
+	shift
+	continue;;
+
+    --version) echo "$0 $scriptversion"; exit 0;;
+
+    *)  # When -d is used, all remaining arguments are directories to create.
+	# When -t is used, the destination is already specified.
+	test -n "$dir_arg$dstarg" && break
+        # Otherwise, the last argument is the destination.  Remove it from $@.
+	for arg
+	do
+          if test -n "$dstarg"; then
+	    # $@ is not empty: it contains at least $arg.
+	    set fnord "$@" "$dstarg"
+	    shift # fnord
+	  fi
+	  shift # arg
+	  dstarg=$arg
+	done
+	break;;
+  esac
+done
+
+if test -z "$1"; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+for src
+do
+  # Protect names starting with `-'.
+  case $src in
+    -*) src=./$src ;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    src=
+
+    if test -d "$dst"; then
+      mkdircmd=:
+      chmodcmd=
+    else
+      mkdircmd=$mkdirprog
+    fi
+  else
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dstarg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+
+    dst=$dstarg
+    # Protect names starting with `-'.
+    case $dst in
+      -*) dst=./$dst ;;
+    esac
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+	echo "$0: $dstarg: Is a directory" >&2
+	exit 1
+      fi
+      dst=$dst/`basename "$src"`
+    fi
+  fi
+
+  # This sed command emulates the dirname command.
+  dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
+
+  # Make sure that the destination directory exists.
+
+  # Skip lots of stat calls in the usual case.
+  if test ! -d "$dstdir"; then
+    defaultIFS='
+	 '
+    IFS="${IFS-$defaultIFS}"
+
+    oIFS=$IFS
+    # Some sh's can't handle IFS=/ for some reason.
+    IFS='%'
+    set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
+    shift
+    IFS=$oIFS
+
+    pathcomp=
+
+    while test $# -ne 0 ; do
+      pathcomp=$pathcomp$1
+      shift
+      if test ! -d "$pathcomp"; then
+        $mkdirprog "$pathcomp"
+	# mkdir can fail with a `File exist' error in case several
+	# install-sh are creating the directory concurrently.  This
+	# is OK.
+	test -d "$pathcomp" || exit
+      fi
+      pathcomp=$pathcomp/
+    done
+  fi
+
+  if test -n "$dir_arg"; then
+    $doit $mkdircmd "$dst" \
+      && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
+      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
+      && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
+      && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
+
+  else
+    dstfile=`basename "$dst"`
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+    trap '(exit $?); exit' 1 2 13 15
+
+    # Copy the file name to the temp name.
+    $doit $cpprog "$src" "$dsttmp" &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+      && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+      && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
+
+    # Now rename the file to the real destination.
+    { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
+      || {
+	   # The rename failed, perhaps because mv can't rename something else
+	   # to itself, or perhaps because mv is so ancient that it does not
+	   # support -f.
+
+	   # Now remove or move aside any old file at destination location.
+	   # We try this two ways since rm can't unlink itself on some
+	   # systems and the destination file might be busy for other
+	   # reasons.  In this case, the final cleanup might fail but the new
+	   # file should still install successfully.
+	   {
+	     if test -f "$dstdir/$dstfile"; then
+	       $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
+	       || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
+	       || {
+		 echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
+		 (exit 1); exit
+	       }
+	     else
+	       :
+	     fi
+	   } &&
+
+	   # Now rename the file to the real destination.
+	   $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
+	 }
+    }
+  fi || { (exit 1); exit; }
+done
+
+# The final little trick to "correctly" pass the exit status to the exit trap.
+{
+  (exit 0); exit
+}
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/userspace/libebtc/ltmain.sh b/userspace/libebtc/ltmain.sh
new file mode 100755
index 0000000..274587e
--- /dev/null
+++ b/userspace/libebtc/ltmain.sh
@@ -0,0 +1,6426 @@
+# ltmain.sh - Provide generalized library-building support services.
+# NOTE: Changing this file will not affect anything until you rerun configure.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
+# Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+basename="s,^.*/,,g"
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+# The name of this program:
+progname=`echo "$progpath" | $SED $basename`
+modename="$progname"
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=1.5.6
+TIMESTAMP=" (1.1220.2.95 2004/04/11 05:50:42) Debian$Rev: 220 $"
+
+
+# Check that we have a working $echo.
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell, and then maybe $echo will work.
+  exec $SHELL "$progpath" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit $EXIT_SUCCESS
+fi
+
+default_mode=
+help="Try \`$progname --help' for more information."
+magic="%%%MAGIC variable%%%"
+mkdir="mkdir"
+mv="mv -f"
+rm="rm -f"
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g'
+# test EBCDIC or ASCII
+case `echo A|tr A '\301'` in
+ A) # EBCDIC based system
+  SP2NL="tr '\100' '\n'"
+  NL2SP="tr '\r\n' '\100\100'"
+  ;;
+ *) # Assume ASCII based system
+  SP2NL="tr '\040' '\012'"
+  NL2SP="tr '\015\012' '\040\040'"
+  ;;
+esac
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+# We save the old values to restore during execute mode.
+if test "${LC_ALL+set}" = set; then
+  save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL
+fi
+if test "${LANG+set}" = set; then
+  save_LANG="$LANG"; LANG=C; export LANG
+fi
+
+# Make sure IFS has a sensible default
+: ${IFS=" 	
+"}
+
+if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+  $echo "$modename: not configured to build any kind of library" 1>&2
+  $echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+  exit $EXIT_FAILURE
+fi
+
+# Global variables.
+mode=$default_mode
+nonopt=
+prev=
+prevopt=
+run=
+show="$echo"
+show_help=
+execute_dlfiles=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+
+#####################################
+# Shell function definitions:
+# This seems to be the best place for them
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+func_win32_libid () {
+  win32_libid_type="unknown"
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \
+      $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
+      win32_nmres=`eval $NM -f posix -A $1 | \
+	sed -n -e '1,100{/ I /{x;/import/!{s/^/import/;h;p;};x;};}'`
+      if test "X$win32_nmres" = "Ximport" ; then
+        win32_libid_type="x86 archive import"
+      else
+        win32_libid_type="x86 archive static"
+      fi
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $echo $win32_libid_type
+}
+
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag () {
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+	case $arg in
+	  *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	  arg="\"$arg\""
+	  ;;
+	esac
+	CC_quoted="$CC_quoted $arg"
+      done
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+	for z in $available_tags; do
+	  if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+	    # Evaluate the configuration.
+	    eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+	    CC_quoted=
+	    for arg in $CC; do
+	    # Double-quote args containing other shell metacharacters.
+	    case $arg in
+	      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	      arg="\"$arg\""
+	      ;;
+	    esac
+	    CC_quoted="$CC_quoted $arg"
+	  done
+	    case "$@ " in
+	      " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*)
+	      # The compiler in the base compile command matches
+	      # the one in the tagged configuration.
+	      # Assume this is the tagged configuration we want.
+	      tagname=$z
+	      break
+	      ;;
+	    esac
+	  fi
+	done
+	# If $tagname still isn't set, then no tagged configuration
+	# was found and let the user know that the "--tag" command
+	# line option must be used.
+	if test -z "$tagname"; then
+	  $echo "$modename: unable to infer tagged configuration"
+	  $echo "$modename: specify a tag with \`--tag'" 1>&2
+	  exit $EXIT_FAILURE
+#        else
+#          $echo "$modename: using $tagname tagged configuration"
+	fi
+	;;
+      esac
+    fi
+}
+# End of Shell function definitions
+#####################################
+
+# Darwin sucks
+eval std_shrext=\"$shrext_cmds\"
+
+# Parse our command line options once, thoroughly.
+while test "$#" -gt 0
+do
+  arg="$1"
+  shift
+
+  case $arg in
+  -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$prev"; then
+    case $prev in
+    execute_dlfiles)
+      execute_dlfiles="$execute_dlfiles $arg"
+      ;;
+    tag)
+      tagname="$arg"
+      preserve_args="${preserve_args}=$arg"
+
+      # Check whether tagname contains only valid characters
+      case $tagname in
+      *[!-_A-Za-z0-9,/]*)
+	$echo "$progname: invalid tag name: $tagname" 1>&2
+	exit $EXIT_FAILURE
+	;;
+      esac
+
+      case $tagname in
+      CC)
+	# Don't test for the "default" C tag, as we know, it's there, but
+	# not specially marked.
+	;;
+      *)
+	if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then
+	  taglist="$taglist $tagname"
+	  # Evaluate the configuration.
+	  eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`"
+	else
+	  $echo "$progname: ignoring unknown tag $tagname" 1>&2
+	fi
+	;;
+      esac
+      ;;
+    *)
+      eval "$prev=\$arg"
+      ;;
+    esac
+
+    prev=
+    prevopt=
+    continue
+  fi
+
+  # Have we seen a non-optional argument yet?
+  case $arg in
+  --help)
+    show_help=yes
+    ;;
+
+  --version)
+    $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"
+    $echo
+    $echo "Copyright (C) 2003  Free Software Foundation, Inc."
+    $echo "This is free software; see the source for copying conditions.  There is NO"
+    $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+    exit $EXIT_SUCCESS
+    ;;
+
+  --config)
+    ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath"
+    done
+    exit $EXIT_SUCCESS
+    ;;
+
+  --debug)
+    $echo "$progname: enabling shell trace mode"
+    set -x
+    preserve_args="$preserve_args $arg"
+    ;;
+
+  --dry-run | -n)
+    run=:
+    ;;
+
+  --features)
+    $echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      $echo "enable shared libraries"
+    else
+      $echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      $echo "enable static libraries"
+    else
+      $echo "disable static libraries"
+    fi
+    exit $EXIT_SUCCESS
+    ;;
+
+  --finish) mode="finish" ;;
+
+  --mode) prevopt="--mode" prev=mode ;;
+  --mode=*) mode="$optarg" ;;
+
+  --preserve-dup-deps) duplicate_deps="yes" ;;
+
+  --quiet | --silent)
+    show=:
+    preserve_args="$preserve_args $arg"
+    ;;
+
+  --tag) prevopt="--tag" prev=tag ;;
+  --tag=*)
+    set tag "$optarg" ${1+"$@"}
+    shift
+    prev=tag
+    preserve_args="$preserve_args --tag"
+    ;;
+
+  -dlopen)
+    prevopt="-dlopen"
+    prev=execute_dlfiles
+    ;;
+
+  -*)
+    $echo "$modename: unrecognized option \`$arg'" 1>&2
+    $echo "$help" 1>&2
+    exit $EXIT_FAILURE
+    ;;
+
+  *)
+    nonopt="$arg"
+    break
+    ;;
+  esac
+done
+
+if test -n "$prevopt"; then
+  $echo "$modename: option \`$prevopt' requires an argument" 1>&2
+  $echo "$help" 1>&2
+  exit $EXIT_FAILURE
+fi
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+if test -z "$show_help"; then
+
+  # Infer the operation mode.
+  if test -z "$mode"; then
+    $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2
+    $echo "*** Future versions of Libtool will require -mode=MODE be specified." 1>&2
+    case $nonopt in
+    *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*)
+      mode=link
+      for arg
+      do
+	case $arg in
+	-c)
+	   mode=compile
+	   break
+	   ;;
+	esac
+      done
+      ;;
+    *db | *dbx | *strace | *truss)
+      mode=execute
+      ;;
+    *install*|cp|mv)
+      mode=install
+      ;;
+    *rm)
+      mode=uninstall
+      ;;
+    *)
+      # If we have no mode, but dlfiles were specified, then do execute mode.
+      test -n "$execute_dlfiles" && mode=execute
+
+      # Just use the default operation mode.
+      if test -z "$mode"; then
+	if test -n "$nonopt"; then
+	  $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2
+	else
+	  $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2
+	fi
+      fi
+      ;;
+    esac
+  fi
+
+  # Only execute mode is allowed to have -dlopen flags.
+  if test -n "$execute_dlfiles" && test "$mode" != execute; then
+    $echo "$modename: unrecognized option \`-dlopen'" 1>&2
+    $echo "$help" 1>&2
+    exit $EXIT_FAILURE
+  fi
+
+  # Change the help message to a mode-specific one.
+  generic_help="$help"
+  help="Try \`$modename --help --mode=$mode' for more information."
+
+  # These modes are in order of execution frequency so that they run quickly.
+  case $mode in
+  # libtool compile mode
+  compile)
+    modename="$modename: compile"
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile="$nonopt"  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+
+    for arg
+    do
+      case "$arg_mode" in
+      arg  )
+	# do not "continue".  Instead, add this to base_compile
+	lastarg="$arg"
+	arg_mode=normal
+	;;
+
+      target )
+	libobj="$arg"
+	arg_mode=normal
+	continue
+	;;
+
+      normal )
+	# Accept any command-line options.
+	case $arg in
+	-o)
+	  if test -n "$libobj" ; then
+	    $echo "$modename: you cannot specify \`-o' more than once" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	  arg_mode=target
+	  continue
+	  ;;
+
+	-static | -prefer-pic | -prefer-non-pic)
+	  later="$later $arg"
+	  continue
+	  ;;
+
+	-no-suppress)
+	  suppress_opt=no
+	  continue
+	  ;;
+
+	-Xcompiler)
+	  arg_mode=arg  #  the next one goes into the "base_compile" arg list
+	  continue      #  The current "srcfile" will either be retained or
+	  ;;            #  replaced later.  I would guess that would be a bug.
+
+	-Wc,*)
+	  args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"`
+	  lastarg=
+	  save_ifs="$IFS"; IFS=','
+ 	  for arg in $args; do
+	    IFS="$save_ifs"
+
+	    # Double-quote args containing other shell metacharacters.
+	    # Many Bourne shells cannot handle close brackets correctly
+	    # in scan sets, so we specify it separately.
+	    case $arg in
+	      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	      arg="\"$arg\""
+	      ;;
+	    esac
+	    lastarg="$lastarg $arg"
+	  done
+	  IFS="$save_ifs"
+	  lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"`
+
+	  # Add the arguments to base_compile.
+	  base_compile="$base_compile $lastarg"
+	  continue
+	  ;;
+
+	* )
+	  # Accept the current argument as the source file.
+	  # The previous "srcfile" becomes the current argument.
+	  #
+	  lastarg="$srcfile"
+	  srcfile="$arg"
+	  ;;
+	esac  #  case $arg
+	;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"`
+
+      case $lastarg in
+      # Double-quote args containing other shell metacharacters.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	lastarg="\"$lastarg\""
+	;;
+      esac
+
+      base_compile="$base_compile $lastarg"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      $echo "$modename: you must specify an argument for -Xcompile"
+      exit $EXIT_FAILURE
+      ;;
+    target)
+      $echo "$modename: you must specify a target with \`-o'" 1>&2
+      exit $EXIT_FAILURE
+      ;;
+    *)
+      # Get the name of the library object.
+      [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'`
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    xform='[cCFSifmso]'
+    case $libobj in
+    *.ada) xform=ada ;;
+    *.adb) xform=adb ;;
+    *.ads) xform=ads ;;
+    *.asm) xform=asm ;;
+    *.c++) xform=c++ ;;
+    *.cc) xform=cc ;;
+    *.ii) xform=ii ;;
+    *.class) xform=class ;;
+    *.cpp) xform=cpp ;;
+    *.cxx) xform=cxx ;;
+    *.f90) xform=f90 ;;
+    *.for) xform=for ;;
+    *.java) xform=java ;;
+    esac
+
+    libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"`
+
+    case $libobj in
+    *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;;
+    *)
+      $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2
+      exit $EXIT_FAILURE
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -static)
+	build_old_libs=yes
+	continue
+	;;
+
+      -prefer-pic)
+	pic_mode=yes
+	continue
+	;;
+
+      -prefer-non-pic)
+	pic_mode=no
+	continue
+	;;
+      esac
+    done
+
+    objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'`
+    xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'`
+    if test "X$xdir" = "X$obj"; then
+      xdir=
+    else
+      xdir=$xdir/
+    fi
+    lobj=${xdir}$objdir/$objname
+
+    if test -z "$base_compile"; then
+      $echo "$modename: you must specify a compilation command" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    $run $rm $removelist
+    trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+      removelist="$removelist $output_obj $lockfile"
+      trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $run ln "$progpath" "$lockfile" 2>/dev/null; do
+	$show "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+	$echo "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$run $rm $removelist
+	exit $EXIT_FAILURE
+      fi
+      $echo $srcfile > "$lockfile"
+    fi
+
+    if test -n "$fix_srcfile_path"; then
+      eval srcfile=\"$fix_srcfile_path\"
+    fi
+
+    $run $rm "$libobj" "${libobj}T"
+
+    # Create a libtool object file (analogous to a ".la" file),
+    # but don't create it if we're doing a dry run.
+    test -z "$run" && cat > ${libobj}T <<EOF
+# $libobj - a libtool object file
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+EOF
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+	command="$base_compile $srcfile $pic_flag"
+      else
+	# Don't build PIC code
+	command="$base_compile $srcfile"
+      fi
+
+      if test ! -d "${xdir}$objdir"; then
+	$show "$mkdir ${xdir}$objdir"
+	$run $mkdir ${xdir}$objdir
+	status=$?
+	if test "$status" -ne 0 && test ! -d "${xdir}$objdir"; then
+	  exit $status
+	fi
+      fi
+
+      if test -z "$output_obj"; then
+	# Place PIC objects in $objdir
+	command="$command -o $lobj"
+      fi
+
+      $run $rm "$lobj" "$output_obj"
+
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+	test -n "$output_obj" && $run $rm $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$run $rm $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+	$show "$mv $output_obj $lobj"
+	if $run $mv $output_obj $lobj; then :
+	else
+	  error=$?
+	  $run $rm $removelist
+	  exit $error
+	fi
+      fi
+
+      # Append the name of the PIC object to the libtool object file.
+      test -z "$run" && cat >> ${libobj}T <<EOF
+pic_object='$objdir/$objname'
+
+EOF
+
+      # Allow error messages only from the first compilation.
+      if test "$suppress_opt" = yes; then
+        suppress_output=' >/dev/null 2>&1'
+      fi
+    else
+      # No PIC object so indicate it doesn't exist in the libtool
+      # object file.
+      test -z "$run" && cat >> ${libobj}T <<EOF
+pic_object=none
+
+EOF
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+	# Don't build PIC code
+	command="$base_compile $srcfile"
+      else
+	command="$base_compile $srcfile $pic_flag"
+      fi
+      if test "$compiler_c_o" = yes; then
+	command="$command -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      command="$command$suppress_output"
+      $run $rm "$obj" "$output_obj"
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+	$run $rm $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$run $rm $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+	$show "$mv $output_obj $obj"
+	if $run $mv $output_obj $obj; then :
+	else
+	  error=$?
+	  $run $rm $removelist
+	  exit $error
+	fi
+      fi
+
+      # Append the name of the non-PIC object the libtool object file.
+      # Only append if the libtool object file exists.
+      test -z "$run" && cat >> ${libobj}T <<EOF
+# Name of the non-PIC object.
+non_pic_object='$objname'
+
+EOF
+    else
+      # Append the name of the non-PIC object the libtool object file.
+      # Only append if the libtool object file exists.
+      test -z "$run" && cat >> ${libobj}T <<EOF
+# Name of the non-PIC object.
+non_pic_object=none
+
+EOF
+    fi
+
+    $run $mv "${libobj}T" "${libobj}"
+
+    # Unlock the critical section if it was locked
+    if test "$need_locks" != no; then
+      $run $rm "$lockfile"
+    fi
+
+    exit $EXIT_SUCCESS
+    ;;
+
+  # libtool link mode
+  link | relink)
+    modename="$modename: link"
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args="$nonopt"
+    base_compile="$nonopt $@"
+    compile_command="$nonopt"
+    finalize_command="$nonopt"
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+
+    avoid_version=no
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -all-static | -static)
+	if test "X$arg" = "X-all-static"; then
+	  if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+	    $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2
+	  fi
+	  if test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	else
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	fi
+	build_libtool_libs=no
+	build_old_libs=yes
+	prefer_static_libs=yes
+	break
+	;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg="$1"
+      shift
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test
+	;;
+      *) qarg=$arg ;;
+      esac
+      libtool_args="$libtool_args $qarg"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+	case $prev in
+	output)
+	  compile_command="$compile_command @OUTPUT@"
+	  finalize_command="$finalize_command @OUTPUT@"
+	  ;;
+	esac
+
+	case $prev in
+	dlfiles|dlprefiles)
+	  if test "$preload" = no; then
+	    # Add the symbol object into the linking commands.
+	    compile_command="$compile_command @SYMFILE@"
+	    finalize_command="$finalize_command @SYMFILE@"
+	    preload=yes
+	  fi
+	  case $arg in
+	  *.la | *.lo) ;;  # We handle these cases below.
+	  force)
+	    if test "$dlself" = no; then
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  self)
+	    if test "$prev" = dlprefiles; then
+	      dlself=yes
+	    elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+	      dlself=yes
+	    else
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  *)
+	    if test "$prev" = dlfiles; then
+	      dlfiles="$dlfiles $arg"
+	    else
+	      dlprefiles="$dlprefiles $arg"
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  esac
+	  ;;
+	expsyms)
+	  export_symbols="$arg"
+	  if test ! -f "$arg"; then
+	    $echo "$modename: symbol file \`$arg' does not exist"
+	    exit $EXIT_FAILURE
+	  fi
+	  prev=
+	  continue
+	  ;;
+	expsyms_regex)
+	  export_symbols_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	inst_prefix)
+	  inst_prefix_dir="$arg"
+	  prev=
+	  continue
+	  ;;
+	precious_regex)
+	  precious_files_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	release)
+	  release="-$arg"
+	  prev=
+	  continue
+	  ;;
+	objectlist)
+	  if test -f "$arg"; then
+	    save_arg=$arg
+	    moreargs=
+	    for fil in `cat $save_arg`
+	    do
+#	      moreargs="$moreargs $fil"
+	      arg=$fil
+	      # A libtool-controlled object.
+
+	      # Check to see that this really is a libtool object.
+	      if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+		pic_object=
+		non_pic_object=
+
+		# Read the .lo file
+		# If there is no directory component, then add one.
+		case $arg in
+		*/* | *\\*) . $arg ;;
+		*) . ./$arg ;;
+		esac
+
+		if test -z "$pic_object" || \
+		   test -z "$non_pic_object" ||
+		   test "$pic_object" = none && \
+		   test "$non_pic_object" = none; then
+		  $echo "$modename: cannot find name of object for \`$arg'" 1>&2
+		  exit $EXIT_FAILURE
+		fi
+
+		# Extract subdirectory from the argument.
+		xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+		if test "X$xdir" = "X$arg"; then
+		  xdir=
+		else
+		  xdir="$xdir/"
+		fi
+
+		if test "$pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  pic_object="$xdir$pic_object"
+
+		  if test "$prev" = dlfiles; then
+		    if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		      dlfiles="$dlfiles $pic_object"
+		      prev=
+		      continue
+		    else
+		      # If libtool objects are unsupported, then we need to preload.
+		      prev=dlprefiles
+		    fi
+		  fi
+
+		  # CHECK ME:  I think I busted this.  -Ossama
+		  if test "$prev" = dlprefiles; then
+		    # Preload the old-style object.
+		    dlprefiles="$dlprefiles $pic_object"
+		    prev=
+		  fi
+
+		  # A PIC object.
+		  libobjs="$libobjs $pic_object"
+		  arg="$pic_object"
+		fi
+
+		# Non-PIC object.
+		if test "$non_pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  non_pic_object="$xdir$non_pic_object"
+
+		  # A standard non-PIC object
+		  non_pic_objects="$non_pic_objects $non_pic_object"
+		  if test -z "$pic_object" || test "$pic_object" = none ; then
+		    arg="$non_pic_object"
+		  fi
+		fi
+	      else
+		# Only an error if not doing a dry-run.
+		if test -z "$run"; then
+		  $echo "$modename: \`$arg' is not a valid libtool object" 1>&2
+		  exit $EXIT_FAILURE
+		else
+		  # Dry-run case.
+
+		  # Extract subdirectory from the argument.
+		  xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+		  if test "X$xdir" = "X$arg"; then
+		    xdir=
+		  else
+		    xdir="$xdir/"
+		  fi
+
+		  pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"`
+		  non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"`
+		  libobjs="$libobjs $pic_object"
+		  non_pic_objects="$non_pic_objects $non_pic_object"
+		fi
+	      fi
+	    done
+	  else
+	    $echo "$modename: link input file \`$save_arg' does not exist"
+	    exit $EXIT_FAILURE
+	  fi
+	  arg=$save_arg
+	  prev=
+	  continue
+	  ;;
+	rpath | xrpath)
+	  # We need an absolute path.
+	  case $arg in
+	  [\\/]* | [A-Za-z]:[\\/]*) ;;
+	  *)
+	    $echo "$modename: only absolute run-paths are allowed" 1>&2
+	    exit $EXIT_FAILURE
+	    ;;
+	  esac
+	  if test "$prev" = rpath; then
+	    case "$rpath " in
+	    *" $arg "*) ;;
+	    *) rpath="$rpath $arg" ;;
+	    esac
+	  else
+	    case "$xrpath " in
+	    *" $arg "*) ;;
+	    *) xrpath="$xrpath $arg" ;;
+	    esac
+	  fi
+	  prev=
+	  continue
+	  ;;
+	xcompiler)
+	  compiler_flags="$compiler_flags $qarg"
+	  prev=
+	  compile_command="$compile_command $qarg"
+	  finalize_command="$finalize_command $qarg"
+	  continue
+	  ;;
+	xlinker)
+	  linker_flags="$linker_flags $qarg"
+	  compiler_flags="$compiler_flags $wl$qarg"
+	  prev=
+	  compile_command="$compile_command $wl$qarg"
+	  finalize_command="$finalize_command $wl$qarg"
+	  continue
+	  ;;
+	xcclinker)
+	  linker_flags="$linker_flags $qarg"
+	  compiler_flags="$compiler_flags $qarg"
+	  prev=
+	  compile_command="$compile_command $qarg"
+	  finalize_command="$finalize_command $qarg"
+	  continue
+	  ;;
+	shrext)
+  	  shrext_cmds="$arg"
+	  prev=
+	  continue
+	  ;;
+	*)
+	  eval "$prev=\"\$arg\""
+	  prev=
+	  continue
+	  ;;
+	esac
+      fi # test -n "$prev"
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+	if test -n "$link_static_flag"; then
+	  compile_command="$compile_command $link_static_flag"
+	  finalize_command="$finalize_command $link_static_flag"
+	fi
+	continue
+	;;
+
+      -allow-undefined)
+	# FIXME: remove this flag sometime in the future.
+	$echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2
+	continue
+	;;
+
+      -avoid-version)
+	avoid_version=yes
+	continue
+	;;
+
+      -dlopen)
+	prev=dlfiles
+	continue
+	;;
+
+      -dlpreopen)
+	prev=dlprefiles
+	continue
+	;;
+
+      -export-dynamic)
+	export_dynamic=yes
+	continue
+	;;
+
+      -export-symbols | -export-symbols-regex)
+	if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+	  $echo "$modename: more than one -exported-symbols argument is not allowed"
+	  exit $EXIT_FAILURE
+	fi
+	if test "X$arg" = "X-export-symbols"; then
+	  prev=expsyms
+	else
+	  prev=expsyms_regex
+	fi
+	continue
+	;;
+
+      -inst-prefix-dir)
+	prev=inst_prefix
+	continue
+	;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+	case $with_gcc/$host in
+	no/*-*-irix* | /*-*-irix*)
+	  compile_command="$compile_command $arg"
+	  finalize_command="$finalize_command $arg"
+	  ;;
+	esac
+	continue
+	;;
+
+      -L*)
+	dir=`$echo "X$arg" | $Xsed -e 's/^-L//'`
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	*)
+	  absdir=`cd "$dir" && pwd`
+	  if test -z "$absdir"; then
+	    $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	  dir="$absdir"
+	  ;;
+	esac
+	case "$deplibs " in
+	*" -L$dir "*) ;;
+	*)
+	  deplibs="$deplibs -L$dir"
+	  lib_search_path="$lib_search_path $dir"
+	  ;;
+	esac
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+	  case :$dllsearchpath: in
+	  *":$dir:"*) ;;
+	  *) dllsearchpath="$dllsearchpath:$dir";;
+	  esac
+	  ;;
+	esac
+	continue
+	;;
+
+      -l*)
+	if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+	  case $host in
+	  *-*-cygwin* | *-*-pw32* | *-*-beos*)
+	    # These systems don't actually have a C or math library (as such)
+	    continue
+	    ;;
+	  *-*-mingw* | *-*-os2*)
+	    # These systems don't actually have a C library (as such)
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-openbsd* | *-*-freebsd*)
+	    # Do not include libc due to us having libc/libc_r.
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C and math libraries are in the System framework
+	    deplibs="$deplibs -framework System"
+	    continue
+	  esac
+	elif test "X$arg" = "X-lc_r"; then
+	 case $host in
+	 *-*-openbsd* | *-*-freebsd*)
+	   # Do not include libc_r directly, use -pthread flag.
+	   continue
+	   ;;
+	 esac
+	fi
+	deplibs="$deplibs $arg"
+	continue
+	;;
+
+     -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe)
+	deplibs="$deplibs $arg"
+	continue
+	;;
+
+      -module)
+	module=yes
+	continue
+	;;
+
+      # gcc -m* arguments should be passed to the linker via $compiler_flags
+      # in order to pass architecture information to the linker
+      # (e.g. 32 vs 64-bit).  This may also be accomplished via -Wl,-mfoo
+      # but this is not reliable with gcc because gcc may use -mfoo to
+      # select a different linker, different libraries, etc, while
+      # -Wl,-mfoo simply passes -mfoo to the linker.
+      -m*)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+	case $arg in
+	*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	  arg="\"$arg\""
+	  ;;
+	esac
+        compile_command="$compile_command $arg"
+        finalize_command="$finalize_command $arg"
+        if test "$with_gcc" = "yes" ; then
+          compiler_flags="$compiler_flags $arg"
+        fi
+        continue
+        ;;
+
+      -shrext)
+	prev=shrext
+	continue
+	;;
+
+      -no-fast-install)
+	fast_install=no
+	continue
+	;;
+
+      -no-install)
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+	  # The PATH hackery in wrapper scripts is required on Windows
+	  # in order for the loader to find any dlls it needs.
+	  $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2
+	  $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2
+	  fast_install=no
+	  ;;
+	*) no_install=yes ;;
+	esac
+	continue
+	;;
+
+      -no-undefined)
+	allow_undefined=no
+	continue
+	;;
+
+      -objectlist)
+	prev=objectlist
+	continue
+	;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+	prev=precious_regex
+	continue
+	;;
+
+      -release)
+	prev=release
+	continue
+	;;
+
+      -rpath)
+	prev=rpath
+	continue
+	;;
+
+      -R)
+	prev=xrpath
+	continue
+	;;
+
+      -R*)
+	dir=`$echo "X$arg" | $Xsed -e 's/^-R//'`
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	*)
+	  $echo "$modename: only absolute run-paths are allowed" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+	case "$xrpath " in
+	*" $dir "*) ;;
+	*) xrpath="$xrpath $dir" ;;
+	esac
+	continue
+	;;
+
+      -static)
+	# The effects of -static are defined in a previous loop.
+	# We used to do the same as -all-static on platforms that
+	# didn't have a PIC flag, but the assumption that the effects
+	# would be equivalent was wrong.  It would break on at least
+	# Digital Unix and AIX.
+	continue
+	;;
+
+      -thread-safe)
+	thread_safe=yes
+	continue
+	;;
+
+      -version-info)
+	prev=vinfo
+	continue
+	;;
+      -version-number)
+	prev=vinfo
+	vinfo_number=yes
+	continue
+	;;
+
+      -Wc,*)
+	args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'`
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+	  case $flag in
+	    *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	    flag="\"$flag\""
+	    ;;
+	  esac
+	  arg="$arg $wl$flag"
+	  compiler_flags="$compiler_flags $flag"
+	done
+	IFS="$save_ifs"
+	arg=`$echo "X$arg" | $Xsed -e "s/^ //"`
+	;;
+
+      -Wl,*)
+	args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'`
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+	  case $flag in
+	    *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	    flag="\"$flag\""
+	    ;;
+	  esac
+	  arg="$arg $wl$flag"
+	  compiler_flags="$compiler_flags $wl$flag"
+	  linker_flags="$linker_flags $flag"
+	done
+	IFS="$save_ifs"
+	arg=`$echo "X$arg" | $Xsed -e "s/^ //"`
+	;;
+
+      -Xcompiler)
+	prev=xcompiler
+	continue
+	;;
+
+      -Xlinker)
+	prev=xlinker
+	continue
+	;;
+
+      -XCClinker)
+	prev=xcclinker
+	continue
+	;;
+
+      # Some other compiler flag.
+      -* | +*)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+	case $arg in
+	*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	  arg="\"$arg\""
+	  ;;
+	esac
+	;;
+
+      *.$objext)
+	# A standard object.
+	objs="$objs $arg"
+	;;
+
+      *.lo)
+	# A libtool-controlled object.
+
+	# Check to see that this really is a libtool object.
+	if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+	  pic_object=
+	  non_pic_object=
+
+	  # Read the .lo file
+	  # If there is no directory component, then add one.
+	  case $arg in
+	  */* | *\\*) . $arg ;;
+	  *) . ./$arg ;;
+	  esac
+
+	  if test -z "$pic_object" || \
+	     test -z "$non_pic_object" ||
+	     test "$pic_object" = none && \
+	     test "$non_pic_object" = none; then
+	    $echo "$modename: cannot find name of object for \`$arg'" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+
+	  # Extract subdirectory from the argument.
+	  xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+	  if test "X$xdir" = "X$arg"; then
+	    xdir=
+ 	  else
+	    xdir="$xdir/"
+	  fi
+
+	  if test "$pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    pic_object="$xdir$pic_object"
+
+	    if test "$prev" = dlfiles; then
+	      if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		dlfiles="$dlfiles $pic_object"
+		prev=
+		continue
+	      else
+		# If libtool objects are unsupported, then we need to preload.
+		prev=dlprefiles
+	      fi
+	    fi
+
+	    # CHECK ME:  I think I busted this.  -Ossama
+	    if test "$prev" = dlprefiles; then
+	      # Preload the old-style object.
+	      dlprefiles="$dlprefiles $pic_object"
+	      prev=
+	    fi
+
+	    # A PIC object.
+	    libobjs="$libobjs $pic_object"
+	    arg="$pic_object"
+	  fi
+
+	  # Non-PIC object.
+	  if test "$non_pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    non_pic_object="$xdir$non_pic_object"
+
+	    # A standard non-PIC object
+	    non_pic_objects="$non_pic_objects $non_pic_object"
+	    if test -z "$pic_object" || test "$pic_object" = none ; then
+	      arg="$non_pic_object"
+	    fi
+	  fi
+	else
+	  # Only an error if not doing a dry-run.
+	  if test -z "$run"; then
+	    $echo "$modename: \`$arg' is not a valid libtool object" 1>&2
+	    exit $EXIT_FAILURE
+	  else
+	    # Dry-run case.
+
+	    # Extract subdirectory from the argument.
+	    xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'`
+	    if test "X$xdir" = "X$arg"; then
+	      xdir=
+	    else
+	      xdir="$xdir/"
+	    fi
+
+	    pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"`
+	    non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"`
+	    libobjs="$libobjs $pic_object"
+	    non_pic_objects="$non_pic_objects $non_pic_object"
+	  fi
+	fi
+	;;
+
+      *.$libext)
+	# An archive.
+	deplibs="$deplibs $arg"
+	old_deplibs="$old_deplibs $arg"
+	continue
+	;;
+
+      *.la)
+	# A libtool-controlled library.
+
+	if test "$prev" = dlfiles; then
+	  # This library was specified with -dlopen.
+	  dlfiles="$dlfiles $arg"
+	  prev=
+	elif test "$prev" = dlprefiles; then
+	  # The library was specified with -dlpreopen.
+	  dlprefiles="$dlprefiles $arg"
+	  prev=
+	else
+	  deplibs="$deplibs $arg"
+	fi
+	continue
+	;;
+
+      # Some other compiler argument.
+      *)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+	case $arg in
+	*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+	  arg="\"$arg\""
+	  ;;
+	esac
+	;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+	compile_command="$compile_command $arg"
+	finalize_command="$finalize_command $arg"
+      fi
+    done # argument parsing loop
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prevarg' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      compile_command="$compile_command $arg"
+      finalize_command="$finalize_command $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'`
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+    if test "X$output_objdir" = "X$output"; then
+      output_objdir="$objdir"
+    else
+      output_objdir="$output_objdir/$objdir"
+    fi
+    # Create the object directory.
+    if test ! -d "$output_objdir"; then
+      $show "$mkdir $output_objdir"
+      $run $mkdir $output_objdir
+      status=$?
+      if test "$status" -ne 0 && test ! -d "$output_objdir"; then
+	exit $status
+      fi
+    fi
+
+    # Determine the type of output
+    case $output in
+    "")
+      $echo "$modename: you must specify an output file" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    case $host in
+    *cygwin* | *mingw* | *pw32*)
+      # don't eliminate duplications in $postdeps and $predeps
+      duplicate_compiler_generated_deps=yes
+      ;;
+    *)
+      duplicate_compiler_generated_deps=$duplicate_deps
+      ;;
+    esac
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if test "X$duplicate_deps" = "Xyes" ; then
+	case "$libs " in
+	*" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+	esac
+      fi
+      libs="$libs $deplib"
+    done
+
+    if test "$linkmode" = lib; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then
+	for pre_post_dep in $predeps $postdeps; do
+	  case "$pre_post_deps " in
+	  *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;;
+	  esac
+	  pre_post_deps="$pre_post_deps $pre_post_dep"
+	done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+    case $linkmode in
+    lib)
+	passes="conv link"
+	for file in $dlfiles $dlprefiles; do
+	  case $file in
+	  *.la) ;;
+	  *)
+	    $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2
+	    exit $EXIT_FAILURE
+	    ;;
+	  esac
+	done
+	;;
+    prog)
+	compile_deplibs=
+	finalize_deplibs=
+	alldeplibs=no
+	newdlfiles=
+	newdlprefiles=
+	passes="conv scan dlopen dlpreopen link"
+	;;
+    *)  passes="conv"
+	;;
+    esac
+    for pass in $passes; do
+      if test "$linkmode,$pass" = "lib,link" ||
+	 test "$linkmode,$pass" = "prog,scan"; then
+	libs="$deplibs"
+	deplibs=
+      fi
+      if test "$linkmode" = prog; then
+	case $pass in
+	dlopen) libs="$dlfiles" ;;
+	dlpreopen) libs="$dlprefiles" ;;
+	link)
+	  libs="$deplibs %DEPLIBS%"
+	  test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
+	  ;;
+	esac
+      fi
+      if test "$pass" = dlopen; then
+	# Collect dlpreopened libraries
+	save_deplibs="$deplibs"
+	deplibs=
+      fi
+      for deplib in $libs; do
+	lib=
+	found=no
+	case $deplib in
+	-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe)
+	  if test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    deplibs="$deplib $deplibs"
+	  fi
+	  continue
+	  ;;
+	-l*)
+	  if test "$linkmode" != lib && test "$linkmode" != prog; then
+	    $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2
+	    continue
+	  fi
+	  name=`$echo "X$deplib" | $Xsed -e 's/^-l//'`
+	  for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do
+	    for search_ext in .la $std_shrext .so .a; do
+	      # Search the libtool library
+	      lib="$searchdir/lib${name}${search_ext}"
+	      if test -f "$lib"; then
+		if test "$search_ext" = ".la"; then
+		  found=yes
+		else
+		  found=no
+		fi
+		break 2
+	      fi
+	    done
+	  done
+	  if test "$found" != yes; then
+	    # deplib doesn't seem to be a libtool library
+	    if test "$linkmode,$pass" = "prog,link"; then
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      deplibs="$deplib $deplibs"
+	      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    continue
+	  else # deplib is a libtool library
+	    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+	    # We need to do some special things here, and not later.
+	    if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	      case " $predeps $postdeps " in
+	      *" $deplib "*)
+		if (${SED} -e '2q' $lib |
+                    grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+		  library_names=
+		  old_library=
+		  case $lib in
+		  */* | *\\*) . $lib ;;
+		  *) . ./$lib ;;
+		  esac
+		  for l in $old_library $library_names; do
+		    ll="$l"
+		  done
+		  if test "X$ll" = "X$old_library" ; then # only static version available
+		    found=no
+		    ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'`
+		    test "X$ladir" = "X$lib" && ladir="."
+		    lib=$ladir/$old_library
+		    if test "$linkmode,$pass" = "prog,link"; then
+		      compile_deplibs="$deplib $compile_deplibs"
+		      finalize_deplibs="$deplib $finalize_deplibs"
+		    else
+		      deplibs="$deplib $deplibs"
+		      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+		    fi
+		    continue
+		  fi
+		fi
+	        ;;
+	      *) ;;
+	      esac
+	    fi
+	  fi
+	  ;; # -l
+	-L*)
+	  case $linkmode in
+	  lib)
+	    deplibs="$deplib $deplibs"
+	    test "$pass" = conv && continue
+	    newdependency_libs="$deplib $newdependency_libs"
+	    newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`
+	    ;;
+	  prog)
+	    if test "$pass" = conv; then
+	      deplibs="$deplib $deplibs"
+	      continue
+	    fi
+	    if test "$pass" = scan; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`
+	    ;;
+	  *)
+	    $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2
+	    ;;
+	  esac # linkmode
+	  continue
+	  ;; # -L
+	-R*)
+	  if test "$pass" = link; then
+	    dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'`
+	    # Make sure the xrpath contains only unique directories.
+	    case "$xrpath " in
+	    *" $dir "*) ;;
+	    *) xrpath="$xrpath $dir" ;;
+	    esac
+	  fi
+	  deplibs="$deplib $deplibs"
+	  continue
+	  ;;
+	*.la) lib="$deplib" ;;
+	*.$libext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	    continue
+	  fi
+	  case $linkmode in
+	  lib)
+	    if test "$deplibs_check_method" != pass_all; then
+	      $echo
+	      $echo "*** Warning: Trying to link with static lib archive $deplib."
+	      $echo "*** I have the capability to make that library automatically link in when"
+	      $echo "*** you link to this library.  But I can only do this if you have a"
+	      $echo "*** shared version of the library, which you do not appear to have"
+	      $echo "*** because the file extensions .$libext of this argument makes me believe"
+	      $echo "*** that it is just a static archive that I should not used here."
+	    else
+	      $echo
+	      $echo "*** Warning: Linking the shared library $output against the"
+	      $echo "*** static library $deplib is not portable!"
+	      deplibs="$deplib $deplibs"
+	    fi
+	    continue
+	    ;;
+	  prog)
+	    if test "$pass" != link; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    continue
+	    ;;
+	  esac # linkmode
+	  ;; # *.$libext
+	*.lo | *.$objext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	  elif test "$linkmode" = prog; then
+	    if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+	      # If there is no dlopen support or we're linking statically,
+	      # we need to preload.
+	      newdlprefiles="$newdlprefiles $deplib"
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      newdlfiles="$newdlfiles $deplib"
+	    fi
+	  fi
+	  continue
+	  ;;
+	%DEPLIBS%)
+	  alldeplibs=yes
+	  continue
+	  ;;
+	esac # case $deplib
+	if test "$found" = yes || test -f "$lib"; then :
+	else
+	  $echo "$modename: cannot find the library \`$lib'" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	# Check to see that this really is a libtool archive.
+	if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+	else
+	  $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'`
+	test "X$ladir" = "X$lib" && ladir="."
+
+	dlname=
+	dlopen=
+	dlpreopen=
+	libdir=
+	library_names=
+	old_library=
+	# If the library was installed with an old release of libtool,
+	# it will not redefine variables installed, or shouldnotlink
+	installed=yes
+	shouldnotlink=no
+
+	# Read the .la file
+	case $lib in
+	*/* | *\\*) . $lib ;;
+	*) . ./$lib ;;
+	esac
+
+	if test "$linkmode,$pass" = "lib,link" ||
+	   test "$linkmode,$pass" = "prog,scan" ||
+	   { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+	  test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+	  test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+	fi
+
+	if test "$pass" = conv; then
+	  # Only check for convenience libraries
+	  deplibs="$lib $deplibs"
+	  if test -z "$libdir"; then
+	    if test -z "$old_library"; then
+	      $echo "$modename: cannot find name of link library for \`$lib'" 1>&2
+	      exit $EXIT_FAILURE
+	    fi
+	    # It is a libtool convenience library, so add in its objects.
+	    convenience="$convenience $ladir/$objdir/$old_library"
+	    old_convenience="$old_convenience $ladir/$objdir/$old_library"
+	    tmp_libs=
+	    for deplib in $dependency_libs; do
+	      deplibs="$deplib $deplibs"
+              if test "X$duplicate_deps" = "Xyes" ; then
+	        case "$tmp_libs " in
+	        *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+	        esac
+              fi
+	      tmp_libs="$tmp_libs $deplib"
+	    done
+	  elif test "$linkmode" != prog && test "$linkmode" != lib; then
+	    $echo "$modename: \`$lib' is not a convenience library" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	  continue
+	fi # $pass = conv
+
+
+	# Get the name of the library we link against.
+	linklib=
+	for l in $old_library $library_names; do
+	  linklib="$l"
+	done
+	if test -z "$linklib"; then
+	  $echo "$modename: cannot find name of link library for \`$lib'" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	# This library was specified with -dlopen.
+	if test "$pass" = dlopen; then
+	  if test -z "$libdir"; then
+	    $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	  if test -z "$dlname" ||
+	     test "$dlopen_support" != yes ||
+	     test "$build_libtool_libs" = no; then
+	    # If there is no dlname, no dlopen support or we're linking
+	    # statically, we need to preload.  We also need to preload any
+	    # dependent libraries so libltdl's deplib preloader doesn't
+	    # bomb out in the load deplibs phase.
+	    dlprefiles="$dlprefiles $lib $dependency_libs"
+	  else
+	    newdlfiles="$newdlfiles $lib"
+	  fi
+	  continue
+	fi # $pass = dlopen
+
+	# We need an absolute path.
+	case $ladir in
+	[\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+	*)
+	  abs_ladir=`cd "$ladir" && pwd`
+	  if test -z "$abs_ladir"; then
+	    $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2
+	    $echo "$modename: passing it literally to the linker, although it might fail" 1>&2
+	    abs_ladir="$ladir"
+	  fi
+	  ;;
+	esac
+	laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+
+	# Find the relevant object directory and library name.
+	if test "X$installed" = Xyes; then
+	  if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    $echo "$modename: warning: library \`$lib' was moved." 1>&2
+	    dir="$ladir"
+	    absdir="$abs_ladir"
+	    libdir="$abs_ladir"
+	  else
+	    dir="$libdir"
+	    absdir="$libdir"
+	  fi
+	else
+	  dir="$ladir/$objdir"
+	  absdir="$abs_ladir/$objdir"
+	  # Remove this search path later
+	  notinst_path="$notinst_path $abs_ladir"
+	fi # $installed = yes
+	name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+
+	# This library was specified with -dlpreopen.
+	if test "$pass" = dlpreopen; then
+	  if test -z "$libdir"; then
+	    $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	  # Prefer using a static library (so that no silly _DYNAMIC symbols
+	  # are required to link).
+	  if test -n "$old_library"; then
+	    newdlprefiles="$newdlprefiles $dir/$old_library"
+	  # Otherwise, use the dlname, so that lt_dlopen finds it.
+	  elif test -n "$dlname"; then
+	    newdlprefiles="$newdlprefiles $dir/$dlname"
+	  else
+	    newdlprefiles="$newdlprefiles $dir/$linklib"
+	  fi
+	fi # $pass = dlpreopen
+
+	if test -z "$libdir"; then
+	  # Link the convenience library
+	  if test "$linkmode" = lib; then
+	    deplibs="$dir/$old_library $deplibs"
+	  elif test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$dir/$old_library $compile_deplibs"
+	    finalize_deplibs="$dir/$old_library $finalize_deplibs"
+	  else
+	    deplibs="$lib $deplibs" # used for prog,scan pass
+	  fi
+	  continue
+	fi
+
+
+	if test "$linkmode" = prog && test "$pass" != link; then
+	  newlib_search_path="$newlib_search_path $ladir"
+	  deplibs="$lib $deplibs"
+
+	  linkalldeplibs=no
+	  if test "$link_all_deplibs" != no || test -z "$library_names" ||
+	     test "$build_libtool_libs" = no; then
+	    linkalldeplibs=yes
+	  fi
+
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    case $deplib in
+	    -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test
+	    esac
+	    # Need to link against all dependency_libs?
+	    if test "$linkalldeplibs" = yes; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      # Need to hardcode shared library paths
+	      # or/and link against static libraries
+	      newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    if test "X$duplicate_deps" = "Xyes" ; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+	      esac
+	    fi
+	    tmp_libs="$tmp_libs $deplib"
+	  done # for deplib
+	  continue
+	fi # $linkmode = prog...
+
+	if test "$linkmode,$pass" = "prog,link"; then
+	  if test -n "$library_names" &&
+	     { test "$prefer_static_libs" = no || test -z "$old_library"; }; then
+	    # We need to hardcode the library path
+	    if test -n "$shlibpath_var"; then
+	      # Make sure the rpath contains only unique directories.
+	      case "$temp_rpath " in
+	      *" $dir "*) ;;
+	      *" $absdir "*) ;;
+	      *) temp_rpath="$temp_rpath $dir" ;;
+	      esac
+	    fi
+
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) compile_rpath="$compile_rpath $absdir"
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) finalize_rpath="$finalize_rpath $libdir"
+	      esac
+	      ;;
+	    esac
+	  fi # $linkmode,$pass = prog,link...
+
+	  if test "$alldeplibs" = yes &&
+	     { test "$deplibs_check_method" = pass_all ||
+	       { test "$build_libtool_libs" = yes &&
+		 test -n "$library_names"; }; }; then
+	    # We only need to search for static libraries
+	    continue
+	  fi
+	fi
+
+	link_static=no # Whether the deplib will be linked statically
+	if test -n "$library_names" &&
+	   { test "$prefer_static_libs" = no || test -z "$old_library"; }; then
+	  if test "$installed" = no; then
+	    notinst_deplibs="$notinst_deplibs $lib"
+	    need_relink=yes
+	  fi
+	  # This is a shared library
+
+	  # Warn about portability, can't link against -module's on
+	  # some systems (darwin)
+	  if test "$shouldnotlink" = yes && test "$pass" = link ; then
+	    $echo
+	    if test "$linkmode" = prog; then
+	      $echo "*** Warning: Linking the executable $output against the loadable module"
+	    else
+	      $echo "*** Warning: Linking the shared library $output against the loadable module"
+	    fi
+	    $echo "*** $linklib is not portable!"
+	  fi
+	  if test "$linkmode" = lib &&
+	     test "$hardcode_into_libs" = yes; then
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) compile_rpath="$compile_rpath $absdir"
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) finalize_rpath="$finalize_rpath $libdir"
+	      esac
+	      ;;
+	    esac
+	  fi
+
+	  if test -n "$old_archive_from_expsyms_cmds"; then
+	    # figure out the soname
+	    set dummy $library_names
+	    realname="$2"
+	    shift; shift
+	    libname=`eval \\$echo \"$libname_spec\"`
+	    # use dlname if we got it. it's perfectly good, no?
+	    if test -n "$dlname"; then
+	      soname="$dlname"
+	    elif test -n "$soname_spec"; then
+	      # bleh windows
+	      case $host in
+	      *cygwin* | mingw*)
+		major=`expr $current - $age`
+		versuffix="-$major"
+		;;
+	      esac
+	      eval soname=\"$soname_spec\"
+	    else
+	      soname="$realname"
+	    fi
+
+	    # Make a new name for the extract_expsyms_cmds to use
+	    soroot="$soname"
+	    soname=`$echo $soroot | ${SED} -e 's/^.*\///'`
+	    newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a"
+
+	    # If the library has no export list, then create one now
+	    if test -f "$output_objdir/$soname-def"; then :
+	    else
+	      $show "extracting exported symbol list from \`$soname'"
+	      save_ifs="$IFS"; IFS='~'
+	      cmds=$extract_expsyms_cmds
+	      for cmd in $cmds; do
+		IFS="$save_ifs"
+		eval cmd=\"$cmd\"
+		$show "$cmd"
+		$run eval "$cmd" || exit $?
+	      done
+	      IFS="$save_ifs"
+	    fi
+
+	    # Create $newlib
+	    if test -f "$output_objdir/$newlib"; then :; else
+	      $show "generating import library for \`$soname'"
+	      save_ifs="$IFS"; IFS='~'
+	      cmds=$old_archive_from_expsyms_cmds
+	      for cmd in $cmds; do
+		IFS="$save_ifs"
+		eval cmd=\"$cmd\"
+		$show "$cmd"
+		$run eval "$cmd" || exit $?
+	      done
+	      IFS="$save_ifs"
+	    fi
+	    # make sure the library variables are pointing to the new library
+	    dir=$output_objdir
+	    linklib=$newlib
+	  fi # test -n "$old_archive_from_expsyms_cmds"
+
+	  if test "$linkmode" = prog || test "$mode" != relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    lib_linked=yes
+	    case $hardcode_action in
+	    immediate | unsupported)
+	      if test "$hardcode_direct" = no; then
+		add="$dir/$linklib"
+		case $host in
+		  *-*-sco3.2v5* ) add_dir="-L$dir" ;;
+		  *-*-darwin* )
+		    # if the lib is a module then we can not link against
+		    # it, someone is ignoring the new warnings I added
+		    if /usr/bin/file -L $add 2> /dev/null | $EGREP "bundle" >/dev/null ; then
+		      $echo "** Warning, lib $linklib is a module, not a shared library"
+		      if test -z "$old_library" ; then
+		        $echo
+		        $echo "** And there doesn't seem to be a static archive available"
+		        $echo "** The link will probably fail, sorry"
+		      else
+		        add="$dir/$old_library"
+		      fi
+		    fi
+		esac
+	      elif test "$hardcode_minus_L" = no; then
+		case $host in
+		*-*-sunos*) add_shlibpath="$dir" ;;
+		esac
+		add_dir="-L$dir"
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = no; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    relink)
+	      if test "$hardcode_direct" = yes; then
+		add="$dir/$linklib"
+	      elif test "$hardcode_minus_L" = yes; then
+		add_dir="-L$dir"
+		# Try looking first in the location we're being installed to.
+		if test -n "$inst_prefix_dir"; then
+		  case "$libdir" in
+		    [\\/]*)
+		      add_dir="$add_dir -L$inst_prefix_dir$libdir"
+		      ;;
+		  esac
+		fi
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = yes; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    *) lib_linked=no ;;
+	    esac
+
+	    if test "$lib_linked" != yes; then
+	      $echo "$modename: configuration error: unsupported hardcode properties"
+	      exit $EXIT_FAILURE
+	    fi
+
+	    if test -n "$add_shlibpath"; then
+	      case :$compile_shlibpath: in
+	      *":$add_shlibpath:"*) ;;
+	      *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+	      esac
+	    fi
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+	      test -n "$add" && compile_deplibs="$add $compile_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	      if test "$hardcode_direct" != yes && \
+		 test "$hardcode_minus_L" != yes && \
+		 test "$hardcode_shlibpath_var" = yes; then
+		case :$finalize_shlibpath: in
+		*":$libdir:"*) ;;
+		*) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+		esac
+	      fi
+	    fi
+	  fi
+
+	  if test "$linkmode" = prog || test "$mode" = relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    # Finalize command for both is simple: just hardcode it.
+	    if test "$hardcode_direct" = yes; then
+	      add="$libdir/$linklib"
+	    elif test "$hardcode_minus_L" = yes; then
+	      add_dir="-L$libdir"
+	      add="-l$name"
+	    elif test "$hardcode_shlibpath_var" = yes; then
+	      case :$finalize_shlibpath: in
+	      *":$libdir:"*) ;;
+	      *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+	      esac
+	      add="-l$name"
+	    elif test "$hardcode_automatic" = yes; then
+	      if test -n "$inst_prefix_dir" &&
+		 test -f "$inst_prefix_dir$libdir/$linklib" ; then
+	        add="$inst_prefix_dir$libdir/$linklib"
+	      else
+	        add="$libdir/$linklib"
+	      fi
+	    else
+	      # We cannot seem to hardcode it, guess we'll fake it.
+	      add_dir="-L$libdir"
+	      # Try looking first in the location we're being installed to.
+	      if test -n "$inst_prefix_dir"; then
+		case "$libdir" in
+		  [\\/]*)
+		    add_dir="$add_dir -L$inst_prefix_dir$libdir"
+		    ;;
+		esac
+	      fi
+	      add="-l$name"
+	    fi
+
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+	      test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	    fi
+	  fi
+	elif test "$linkmode" = prog; then
+	  # Here we assume that one of hardcode_direct or hardcode_minus_L
+	  # is not unsupported.  This is valid on all known static and
+	  # shared platforms.
+	  if test "$hardcode_direct" != unsupported; then
+	    test -n "$old_library" && linklib="$old_library"
+	    compile_deplibs="$dir/$linklib $compile_deplibs"
+	    finalize_deplibs="$dir/$linklib $finalize_deplibs"
+	  else
+	    compile_deplibs="-l$name -L$dir $compile_deplibs"
+	    finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+	  fi
+	elif test "$build_libtool_libs" = yes; then
+	  # Not a shared library
+	  if test "$deplibs_check_method" != pass_all; then
+	    # We're trying link a shared library against a static one
+	    # but the system doesn't support it.
+
+	    # Just print a warning and add the library to dependency_libs so
+	    # that the program can be linked against the static library.
+	    $echo
+	    $echo "*** Warning: This system can not link to static lib archive $lib."
+	    $echo "*** I have the capability to make that library automatically link in when"
+	    $echo "*** you link to this library.  But I can only do this if you have a"
+	    $echo "*** shared version of the library, which you do not appear to have."
+	    if test "$module" = yes; then
+	      $echo "*** But as you try to build a module library, libtool will still create "
+	      $echo "*** a static module, that should work as long as the dlopening application"
+	      $echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+	      if test -z "$global_symbol_pipe"; then
+		$echo
+		$echo "*** However, this would only work if libtool was able to extract symbol"
+		$echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+		$echo "*** not find such a program.  So, this module is probably useless."
+		$echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	      fi
+	      if test "$build_old_libs" = no; then
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  else
+	    convenience="$convenience $dir/$old_library"
+	    old_convenience="$old_convenience $dir/$old_library"
+	    deplibs="$dir/$old_library $deplibs"
+	    link_static=yes
+	  fi
+	fi # link shared/static library?
+
+	if test "$linkmode" = lib; then
+	  if test -n "$dependency_libs" &&
+	     { test "$hardcode_into_libs" != yes ||
+	       test "$build_old_libs" = yes ||
+	       test "$link_static" = yes; }; then
+	    # Extract -R from dependency_libs
+	    temp_deplibs=
+	    for libdir in $dependency_libs; do
+	      case $libdir in
+	      -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'`
+		   case " $xrpath " in
+		   *" $temp_xrpath "*) ;;
+		   *) xrpath="$xrpath $temp_xrpath";;
+		   esac;;
+	      *) temp_deplibs="$temp_deplibs $libdir";;
+	      esac
+	    done
+	    dependency_libs="$temp_deplibs"
+	  fi
+
+	  newlib_search_path="$newlib_search_path $absdir"
+	  # Link against this library
+	  test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+	  # ... and its dependency_libs
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    newdependency_libs="$deplib $newdependency_libs"
+	    if test "X$duplicate_deps" = "Xyes" ; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+	      esac
+	    fi
+	    tmp_libs="$tmp_libs $deplib"
+	  done
+
+	  if test "$link_all_deplibs" != no; then
+	    # Add the search paths of all dependency libraries
+	    for deplib in $dependency_libs; do
+	      case $deplib in
+	      -L*) path="$deplib" ;;
+	      *.la)
+		dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'`
+		test "X$dir" = "X$deplib" && dir="."
+		# We need an absolute path.
+		case $dir in
+		[\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+		*)
+		  absdir=`cd "$dir" && pwd`
+		  if test -z "$absdir"; then
+		    $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2
+		    absdir="$dir"
+		  fi
+		  ;;
+		esac
+		if grep "^installed=no" $deplib > /dev/null; then
+		  path="$absdir/$objdir"
+		else
+		  eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+		  if test -z "$libdir"; then
+		    $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
+		    exit $EXIT_FAILURE
+		  fi
+		  if test "$absdir" != "$libdir"; then
+		    $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2
+		  fi
+		  path="$absdir"
+		fi
+		depdepl=
+		case $host in
+		*-*-darwin*)
+		  # we do not want to link against static libs,
+		  # but need to link against shared
+		  eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+		  if test -n "$deplibrary_names" ; then
+		    for tmp in $deplibrary_names ; do
+		      depdepl=$tmp
+		    done
+		    if test -f "$path/$depdepl" ; then
+		      depdepl="$path/$depdepl"
+		    fi
+		    # do not add paths which are already there
+		    case " $newlib_search_path " in
+		    *" $path "*) ;;
+		    *) newlib_search_path="$newlib_search_path $path";;
+		    esac
+		  fi
+		  path=""
+		  ;;
+		*)
+		  path="-L$path"
+		  ;;
+		esac
+		;;
+	      -l*)
+		case $host in
+		*-*-darwin*)
+		  # Again, we only want to link against shared libraries
+		  eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"`
+		  for tmp in $newlib_search_path ; do
+		    if test -f "$tmp/lib$tmp_libs.dylib" ; then
+		      eval depdepl="$tmp/lib$tmp_libs.dylib"
+		      break
+		    fi
+		  done
+		  path=""
+		  ;;
+		*) continue ;;
+		esac
+		;;
+	      *) continue ;;
+	      esac
+	      case " $deplibs " in
+	      *" $depdepl "*) ;;
+	      *) deplibs="$depdepl $deplibs" ;;
+	      esac
+	      case " $deplibs " in
+	      *" $path "*) ;;
+	      *) deplibs="$deplibs $path" ;;
+	      esac
+	    done
+	  fi # link_all_deplibs != no
+	fi # linkmode = lib
+      done # for deplib in $libs
+      dependency_libs="$newdependency_libs"
+      if test "$pass" = dlpreopen; then
+	# Link the dlpreopened libraries before other libraries
+	for deplib in $save_deplibs; do
+	  deplibs="$deplib $deplibs"
+	done
+      fi
+      if test "$pass" != dlopen; then
+	if test "$pass" != conv; then
+	  # Make sure lib_search_path contains only unique directories.
+	  lib_search_path=
+	  for dir in $newlib_search_path; do
+	    case "$lib_search_path " in
+	    *" $dir "*) ;;
+	    *) lib_search_path="$lib_search_path $dir" ;;
+	    esac
+	  done
+	  newlib_search_path=
+	fi
+
+	if test "$linkmode,$pass" != "prog,link"; then
+	  vars="deplibs"
+	else
+	  vars="compile_deplibs finalize_deplibs"
+	fi
+	for var in $vars dependency_libs; do
+	  # Add libraries to $var in reverse order
+	  eval tmp_libs=\"\$$var\"
+	  new_libs=
+	  for deplib in $tmp_libs; do
+	    # FIXME: Pedantically, this is the right thing to do, so
+	    #        that some nasty dependency loop isn't accidentally
+	    #        broken:
+	    #new_libs="$deplib $new_libs"
+	    # Pragmatically, this seems to cause very few problems in
+	    # practice:
+	    case $deplib in
+	    -L*) new_libs="$deplib $new_libs" ;;
+	    -R*) ;;
+	    *)
+	      # And here is the reason: when a library appears more
+	      # than once as an explicit dependence of a library, or
+	      # is implicitly linked in more than once by the
+	      # compiler, it is considered special, and multiple
+	      # occurrences thereof are not removed.  Compare this
+	      # with having the same library being listed as a
+	      # dependency of multiple other libraries: in this case,
+	      # we know (pedantically, we assume) the library does not
+	      # need to be listed more than once, so we keep only the
+	      # last copy.  This is not always right, but it is rare
+	      # enough that we require users that really mean to play
+	      # such unportable linking tricks to link the library
+	      # using -Wl,-lname, so that libtool does not consider it
+	      # for duplicate removal.
+	      case " $specialdeplibs " in
+	      *" $deplib "*) new_libs="$deplib $new_libs" ;;
+	      *)
+		case " $new_libs " in
+		*" $deplib "*) ;;
+		*) new_libs="$deplib $new_libs" ;;
+		esac
+		;;
+	      esac
+	      ;;
+	    esac
+	  done
+	  tmp_libs=
+	  for deplib in $new_libs; do
+	    case $deplib in
+	    -L*)
+	      case " $tmp_libs " in
+	      *" $deplib "*) ;;
+	      *) tmp_libs="$tmp_libs $deplib" ;;
+	      esac
+	      ;;
+	    *) tmp_libs="$tmp_libs $deplib" ;;
+	    esac
+	  done
+	  eval $var=\"$tmp_libs\"
+	done # for var
+      fi
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs ; do
+	case " $predeps $postdeps $compiler_lib_search_path " in
+	*" $i "*)
+	  i=""
+	  ;;
+	esac
+	if test -n "$i" ; then
+	  tmp_libs="$tmp_libs $i"
+	fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$deplibs"; then
+	$echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	$echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+	$echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+	$echo "$modename: warning: \`-R' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+	$echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$release"; then
+	$echo "$modename: warning: \`-release' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+	$echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2
+      fi
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      objs="$objs$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+	name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+	eval shared_ext=\"$shrext_cmds\"
+	eval libname=\"$libname_spec\"
+	;;
+      *)
+	if test "$module" = no; then
+	  $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+	if test "$need_lib_prefix" != no; then
+	  # Add the "lib" prefix for modules if required
+	  name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+	  eval shared_ext=\"$shrext_cmds\"
+	  eval libname=\"$libname_spec\"
+	else
+	  libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+	fi
+	;;
+      esac
+
+      if test -n "$objs"; then
+	if test "$deplibs_check_method" != pass_all; then
+	  $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1
+	  exit $EXIT_FAILURE
+	else
+	  $echo
+	  $echo "*** Warning: Linking the shared library $output against the non-libtool"
+	  $echo "*** objects $objs is not portable!"
+	  libobjs="$libobjs $objs"
+	fi
+      fi
+
+      if test "$dlself" != no; then
+	$echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2
+      fi
+
+      set dummy $rpath
+      if test "$#" -gt 2; then
+	$echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2
+      fi
+      install_libdir="$2"
+
+      oldlibs=
+      if test -z "$rpath"; then
+	if test "$build_libtool_libs" = yes; then
+	  # Building a libtool convenience library.
+	  # Some compilers have problems with a `.al' extension so
+	  # convenience libraries should have the same extension an
+	  # archive normally would.
+	  oldlibs="$output_objdir/$libname.$libext $oldlibs"
+	  build_libtool_libs=convenience
+	  build_old_libs=yes
+	fi
+
+	if test -n "$vinfo"; then
+	  $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2
+	fi
+
+	if test -n "$release"; then
+	  $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2
+	fi
+      else
+
+	# Parse the version information argument.
+	save_ifs="$IFS"; IFS=':'
+	set dummy $vinfo 0 0 0
+	IFS="$save_ifs"
+
+	if test -n "$8"; then
+	  $echo "$modename: too many parameters to \`-version-info'" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	# convert absolute version numbers to libtool ages
+	# this retains compatibility with .la files and attempts
+	# to make the code below a bit more comprehensible
+
+	case $vinfo_number in
+	yes)
+	  number_major="$2"
+	  number_minor="$3"
+	  number_revision="$4"
+	  #
+	  # There are really only two kinds -- those that
+	  # use the current revision as the major version
+	  # and those that subtract age and use age as
+	  # a minor version.  But, then there is irix
+	  # which has an extra 1 added just for fun
+	  #
+	  case $version_type in
+	  darwin|linux|osf|windows)
+	    current=`expr $number_major + $number_minor`
+	    age="$number_minor"
+	    revision="$number_revision"
+	    ;;
+	  freebsd-aout|freebsd-elf|sunos)
+	    current="$number_major"
+	    revision="$number_minor"
+	    age="0"
+	    ;;
+	  irix|nonstopux)
+	    current=`expr $number_major + $number_minor - 1`
+	    age="$number_minor"
+	    revision="$number_minor"
+	    ;;
+	  *)
+	    $echo "$modename: unknown library version type \`$version_type'" 1>&2
+	    $echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+	    exit $EXIT_FAILURE
+	    ;;
+	  esac
+	  ;;
+	no)
+	  current="$2"
+	  revision="$3"
+	  age="$4"
+	  ;;
+	esac
+
+	# Check that each of the things are valid numbers.
+	case $current in
+	0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+	*)
+	  $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2
+	  $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+
+	case $revision in
+	0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+	*)
+	  $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2
+	  $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+
+	case $age in
+	0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+	*)
+	  $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2
+	  $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+
+	if test "$age" -gt "$current"; then
+	  $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2
+	  $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	# Calculate the version variables.
+	major=
+	versuffix=
+	verstring=
+	case $version_type in
+	none) ;;
+
+	darwin)
+	  # Like Linux, but with the current version available in
+	  # verstring for coding it into the library header
+	  major=.`expr $current - $age`
+	  versuffix="$major.$age.$revision"
+	  # Darwin ld doesn't like 0 for these options...
+	  minor_current=`expr $current + 1`
+	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+	  ;;
+
+	freebsd-aout)
+	  major=".$current"
+	  versuffix=".$current.$revision";
+	  ;;
+
+	freebsd-elf)
+	  major=".$current"
+	  versuffix=".$current";
+	  ;;
+
+	irix | nonstopux)
+	  major=`expr $current - $age + 1`
+
+	  case $version_type in
+	    nonstopux) verstring_prefix=nonstopux ;;
+	    *)         verstring_prefix=sgi ;;
+	  esac
+	  verstring="$verstring_prefix$major.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$revision
+	  while test "$loop" -ne 0; do
+	    iface=`expr $revision - $loop`
+	    loop=`expr $loop - 1`
+	    verstring="$verstring_prefix$major.$iface:$verstring"
+	  done
+
+	  # Before this point, $major must not contain `.'.
+	  major=.$major
+	  versuffix="$major.$revision"
+	  ;;
+
+	linux)
+	  major=.`expr $current - $age`
+	  versuffix="$major.$age.$revision"
+	  ;;
+
+	osf)
+	  major=.`expr $current - $age`
+	  versuffix=".$current.$age.$revision"
+	  verstring="$current.$age.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$age
+	  while test "$loop" -ne 0; do
+	    iface=`expr $current - $loop`
+	    loop=`expr $loop - 1`
+	    verstring="$verstring:${iface}.0"
+	  done
+
+	  # Make executables depend on our current version.
+	  verstring="$verstring:${current}.0"
+	  ;;
+
+	sunos)
+	  major=".$current"
+	  versuffix=".$current.$revision"
+	  ;;
+
+	windows)
+	  # Use '-' rather than '.', since we only want one
+	  # extension on DOS 8.3 filesystems.
+	  major=`expr $current - $age`
+	  versuffix="-$major"
+	  ;;
+
+	*)
+	  $echo "$modename: unknown library version type \`$version_type'" 1>&2
+	  $echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+
+	# Clear the version info if we defaulted, and they specified a release.
+	if test -z "$vinfo" && test -n "$release"; then
+	  major=
+	  case $version_type in
+	  darwin)
+	    # we can't check for "0.0" in archive_cmds due to quoting
+	    # problems, so we reset it completely
+	    verstring=
+	    ;;
+	  *)
+	    verstring="0.0"
+	    ;;
+	  esac
+	  if test "$need_version" = no; then
+	    versuffix=
+	  else
+	    versuffix=".0.0"
+	  fi
+	fi
+
+	# Remove version info from name if versioning should be avoided
+	if test "$avoid_version" = yes && test "$need_version" = no; then
+	  major=
+	  versuffix=
+	  verstring=""
+	fi
+
+	# Check to see if the archive will have undefined symbols.
+	if test "$allow_undefined" = yes; then
+	  if test "$allow_undefined_flag" = unsupported; then
+	    $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2
+	    build_libtool_libs=no
+	    build_old_libs=yes
+	  fi
+	else
+	  # Don't allow undefined symbols.
+	  allow_undefined_flag="$no_undefined_flag"
+	fi
+      fi
+
+      if test "$mode" != relink; then
+	# Remove our outputs, but don't remove object files since they
+	# may have been created when compiling PIC objects.
+	removelist=
+	tempremovelist=`$echo "$output_objdir/*"`
+	for p in $tempremovelist; do
+	  case $p in
+	    *.$objext)
+	       ;;
+	    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+	       if test "X$precious_files_regex" != "X"; then
+	         if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+	         then
+		   continue
+		 fi
+	       fi
+	       removelist="$removelist $p"
+	       ;;
+	    *) ;;
+	  esac
+	done
+	if test -n "$removelist"; then
+	  $show "${rm}r $removelist"
+	  $run ${rm}r $removelist
+	fi
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+	oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+	# Transform .lo files to .o files.
+	oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      for path in $notinst_path; do
+	lib_search_path=`$echo "$lib_search_path " | ${SED} -e 's% $path % %g'`
+	deplibs=`$echo "$deplibs " | ${SED} -e 's% -L$path % %g'`
+	dependency_libs=`$echo "$dependency_libs " | ${SED} -e 's% -L$path % %g'`
+      done
+
+      if test -n "$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	temp_xrpath=
+	for libdir in $xrpath; do
+	  temp_xrpath="$temp_xrpath -R$libdir"
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) finalize_rpath="$finalize_rpath $libdir" ;;
+	  esac
+	done
+	if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+	  dependency_libs="$temp_xrpath $dependency_libs"
+	fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+	case " $dlprefiles $dlfiles " in
+	*" $lib "*) ;;
+	*) dlfiles="$dlfiles $lib" ;;
+	esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+	case "$dlprefiles " in
+	*" $lib "*) ;;
+	*) dlprefiles="$dlprefiles $lib" ;;
+	esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+	if test -n "$rpath"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*)
+	    # these systems don't actually have a c library (as such)!
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C library is in the System framework
+	    deplibs="$deplibs -framework System"
+	    ;;
+	  *-*-netbsd*)
+	    # Don't link with libc until the a.out ld.so is fixed.
+	    ;;
+	  *-*-openbsd* | *-*-freebsd*)
+	    # Do not include libc due to us having libc/libc_r.
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+ 	  *)
+	    # Add libc to deplibs on all other systems if necessary.
+	    if test "$build_libtool_need_lc" = "yes"; then
+	      deplibs="$deplibs -lc"
+	    fi
+	    ;;
+	  esac
+	fi
+
+	# Transform deplibs into only deplibs that can be linked in shared.
+	name_save=$name
+	libname_save=$libname
+	release_save=$release
+	versuffix_save=$versuffix
+	major_save=$major
+	# I'm not sure if I'm treating the release correctly.  I think
+	# release should show up in the -l (ie -lgmp5) so we don't want to
+	# add it in twice.  Is that correct?
+	release=""
+	versuffix=""
+	major=""
+	newdeplibs=
+	droppeddeps=no
+	case $deplibs_check_method in
+	pass_all)
+	  # Don't check for shared/static.  Everything works.
+	  # This might be a little naive.  We might want to check
+	  # whether the library exists or not.  But this is on
+	  # osf3 & osf4 and I'm not really sure... Just
+	  # implementing what was already the behavior.
+	  newdeplibs=$deplibs
+	  ;;
+	test_compile)
+	  # This code stresses the "libraries are programs" paradigm to its
+	  # limits. Maybe even breaks it.  We compile a program, linking it
+	  # against the deplibs as a proxy for the library.  Then we can check
+	  # whether they linked in statically or dynamically with ldd.
+	  $rm conftest.c
+	  cat > conftest.c <<EOF
+	  int main() { return 0; }
+EOF
+	  $rm conftest
+	  $LTCC -o conftest conftest.c $deplibs
+	  if test "$?" -eq 0 ; then
+	    ldd_output=`ldd conftest`
+	    for i in $deplibs; do
+	      name="`expr $i : '-l\(.*\)'`"
+	      # If $name is empty we are operating on a -L argument.
+              if test "$name" != "" && test "$name" -ne "0"; then
+		if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		  case " $predeps $postdeps " in
+		  *" $i "*)
+		    newdeplibs="$newdeplibs $i"
+		    i=""
+		    ;;
+		  esac
+	        fi
+		if test -n "$i" ; then
+		  libname=`eval \\$echo \"$libname_spec\"`
+		  deplib_matches=`eval \\$echo \"$library_names_spec\"`
+		  set dummy $deplib_matches
+		  deplib_match=$2
+		  if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		    newdeplibs="$newdeplibs $i"
+		  else
+		    droppeddeps=yes
+		    $echo
+		    $echo "*** Warning: dynamic linker does not accept needed library $i."
+		    $echo "*** I have the capability to make that library automatically link in when"
+		    $echo "*** you link to this library.  But I can only do this if you have a"
+		    $echo "*** shared version of the library, which I believe you do not have"
+		    $echo "*** because a test_compile did reveal that the linker did not use it for"
+		    $echo "*** its dynamic dependency list that programs get resolved with at runtime."
+		  fi
+		fi
+	      else
+		newdeplibs="$newdeplibs $i"
+	      fi
+	    done
+	  else
+	    # Error occurred in the first compile.  Let's try to salvage
+	    # the situation: Compile a separate program for each library.
+	    for i in $deplibs; do
+	      name="`expr $i : '-l\(.*\)'`"
+	      # If $name is empty we are operating on a -L argument.
+              if test "$name" != "" && test "$name" != "0"; then
+		$rm conftest
+		$LTCC -o conftest conftest.c $i
+		# Did it work?
+		if test "$?" -eq 0 ; then
+		  ldd_output=`ldd conftest`
+		  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		    case " $predeps $postdeps " in
+		    *" $i "*)
+		      newdeplibs="$newdeplibs $i"
+		      i=""
+		      ;;
+		    esac
+		  fi
+		  if test -n "$i" ; then
+		    libname=`eval \\$echo \"$libname_spec\"`
+		    deplib_matches=`eval \\$echo \"$library_names_spec\"`
+		    set dummy $deplib_matches
+		    deplib_match=$2
+		    if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		      newdeplibs="$newdeplibs $i"
+		    else
+		      droppeddeps=yes
+		      $echo
+		      $echo "*** Warning: dynamic linker does not accept needed library $i."
+		      $echo "*** I have the capability to make that library automatically link in when"
+		      $echo "*** you link to this library.  But I can only do this if you have a"
+		      $echo "*** shared version of the library, which you do not appear to have"
+		      $echo "*** because a test_compile did reveal that the linker did not use this one"
+		      $echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+		    fi
+		  fi
+		else
+		  droppeddeps=yes
+		  $echo
+		  $echo "*** Warning!  Library $i is needed by this library but I was not able to"
+		  $echo "***  make it link in!  You will probably need to install it or some"
+		  $echo "*** library that it depends on before this library will be fully"
+		  $echo "*** functional.  Installing it before continuing would be even better."
+		fi
+	      else
+		newdeplibs="$newdeplibs $i"
+	      fi
+	    done
+	  fi
+	  ;;
+	file_magic*)
+	  set dummy $deplibs_check_method
+	  file_magic_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    name="`expr $a_deplib : '-l\(.*\)'`"
+	    # If $name is empty we are operating on a -L argument.
+            if test "$name" != "" && test  "$name" != "0"; then
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  newdeplibs="$newdeplibs $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval \\$echo \"$libname_spec\"`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+		  for potent_lib in $potential_libs; do
+		      # Follow soft links.
+		      if ls -lLd "$potent_lib" 2>/dev/null \
+			 | grep " -> " >/dev/null; then
+			continue
+		      fi
+		      # The statement above tries to avoid entering an
+		      # endless loop below, in case of cyclic links.
+		      # We might still enter an endless loop, since a link
+		      # loop can be closed while we follow links,
+		      # but so what?
+		      potlib="$potent_lib"
+		      while test -h "$potlib" 2>/dev/null; do
+			potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+			case $potliblink in
+			[\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+			*) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+			esac
+		      done
+		      if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \
+			 | ${SED} 10q \
+			 | $EGREP "$file_magic_regex" > /dev/null; then
+			newdeplibs="$newdeplibs $a_deplib"
+			a_deplib=""
+			break 2
+		      fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		$echo
+		$echo "*** Warning: linker path does not have real file for library $a_deplib."
+		$echo "*** I have the capability to make that library automatically link in when"
+		$echo "*** you link to this library.  But I can only do this if you have a"
+		$echo "*** shared version of the library, which you do not appear to have"
+		$echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $echo "*** with $libname but no candidates were found. (...for file magic test)"
+		else
+		  $echo "*** with $libname and none of the candidates passed a file format test"
+		  $echo "*** using a file magic. Last file checked: $potlib"
+		fi
+	      fi
+	    else
+	      # Add a -L argument.
+	      newdeplibs="$newdeplibs $a_deplib"
+	    fi
+	  done # Gone through all deplibs.
+	  ;;
+	match_pattern*)
+	  set dummy $deplibs_check_method
+	  match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    name="`expr $a_deplib : '-l\(.*\)'`"
+	    # If $name is empty we are operating on a -L argument.
+	    if test -n "$name" && test "$name" != "0"; then
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  newdeplibs="$newdeplibs $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval \\$echo \"$libname_spec\"`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+		  for potent_lib in $potential_libs; do
+		    potlib="$potent_lib" # see symlink-check above in file_magic test
+		    if eval $echo \"$potent_lib\" 2>/dev/null \
+		        | ${SED} 10q \
+		        | $EGREP "$match_pattern_regex" > /dev/null; then
+		      newdeplibs="$newdeplibs $a_deplib"
+		      a_deplib=""
+		      break 2
+		    fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		$echo
+		$echo "*** Warning: linker path does not have real file for library $a_deplib."
+		$echo "*** I have the capability to make that library automatically link in when"
+		$echo "*** you link to this library.  But I can only do this if you have a"
+		$echo "*** shared version of the library, which you do not appear to have"
+		$echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $echo "*** with $libname but no candidates were found. (...for regex pattern test)"
+		else
+		  $echo "*** with $libname and none of the candidates passed a file format test"
+		  $echo "*** using a regex pattern. Last file checked: $potlib"
+		fi
+	      fi
+	    else
+	      # Add a -L argument.
+	      newdeplibs="$newdeplibs $a_deplib"
+	    fi
+	  done # Gone through all deplibs.
+	  ;;
+	none | unknown | *)
+	  newdeplibs=""
+	  tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \
+	    -e 's/ -[LR][^ ]*//g'`
+	  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	    for i in $predeps $postdeps ; do
+	      # can't use Xsed below, because $i might contain '/'
+	      tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"`
+	    done
+	  fi
+	  if $echo "X $tmp_deplibs" | $Xsed -e 's/[ 	]//g' \
+	    | grep . >/dev/null; then
+	    $echo
+	    if test "X$deplibs_check_method" = "Xnone"; then
+	      $echo "*** Warning: inter-library dependencies are not supported in this platform."
+	    else
+	      $echo "*** Warning: inter-library dependencies are not known to be supported."
+	    fi
+	    $echo "*** All declared inter-library dependencies are being dropped."
+	    droppeddeps=yes
+	  fi
+	  ;;
+	esac
+	versuffix=$versuffix_save
+	major=$major_save
+	release=$release_save
+	libname=$libname_save
+	name=$name_save
+
+	case $host in
+	*-*-rhapsody* | *-*-darwin1.[012])
+	  # On Rhapsody replace the C library is the System framework
+	  newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'`
+	  ;;
+	esac
+
+	if test "$droppeddeps" = yes; then
+	  if test "$module" = yes; then
+	    $echo
+	    $echo "*** Warning: libtool could not satisfy all declared inter-library"
+	    $echo "*** dependencies of module $libname.  Therefore, libtool will create"
+	    $echo "*** a static module, that should work as long as the dlopening"
+	    $echo "*** application is linked with the -dlopen flag."
+	    if test -z "$global_symbol_pipe"; then
+	      $echo
+	      $echo "*** However, this would only work if libtool was able to extract symbol"
+	      $echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+	      $echo "*** not find such a program.  So, this module is probably useless."
+	      $echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	    fi
+	    if test "$build_old_libs" = no; then
+	      oldlibs="$output_objdir/$libname.$libext"
+	      build_libtool_libs=module
+	      build_old_libs=yes
+	    else
+	      build_libtool_libs=no
+	    fi
+	  else
+	    $echo "*** The inter-library dependencies that have been dropped here will be"
+	    $echo "*** automatically added whenever a program is linked with this library"
+	    $echo "*** or is declared to -dlopen it."
+
+	    if test "$allow_undefined" = no; then
+	      $echo
+	      $echo "*** Since this library must not contain undefined symbols,"
+	      $echo "*** because either the platform does not support them or"
+	      $echo "*** it was explicitly requested with -no-undefined,"
+	      $echo "*** libtool will only create a static version of it."
+	      if test "$build_old_libs" = no; then
+		oldlibs="$output_objdir/$libname.$libext"
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  fi
+	fi
+	# Done checking deplibs!
+	deplibs=$newdeplibs
+      fi
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+	if test "$hardcode_into_libs" = yes; then
+	  # Hardcode the library paths
+	  hardcode_libdirs=
+	  dep_rpath=
+	  rpath="$finalize_rpath"
+	  test "$mode" != relink && rpath="$compile_rpath$rpath"
+	  for libdir in $rpath; do
+	    if test -n "$hardcode_libdir_flag_spec"; then
+	      if test -n "$hardcode_libdir_separator"; then
+		if test -z "$hardcode_libdirs"; then
+		  hardcode_libdirs="$libdir"
+		else
+		  # Just accumulate the unique libdirs.
+		  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+		  *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		    ;;
+		  *)
+		    hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+		    ;;
+		  esac
+		fi
+	      else
+		eval flag=\"$hardcode_libdir_flag_spec\"
+		dep_rpath="$dep_rpath $flag"
+	      fi
+	    elif test -n "$runpath_var"; then
+	      case "$perm_rpath " in
+	      *" $libdir "*) ;;
+	      *) perm_rpath="$perm_rpath $libdir" ;;
+	      esac
+	    fi
+	  done
+	  # Substitute the hardcoded libdirs into the rpath.
+	  if test -n "$hardcode_libdir_separator" &&
+	     test -n "$hardcode_libdirs"; then
+	    libdir="$hardcode_libdirs"
+	    if test -n "$hardcode_libdir_flag_spec_ld"; then
+	      eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
+	    else
+	      eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+	    fi
+	  fi
+	  if test -n "$runpath_var" && test -n "$perm_rpath"; then
+	    # We should set the runpath_var.
+	    rpath=
+	    for dir in $perm_rpath; do
+	      rpath="$rpath$dir:"
+	    done
+	    eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+	  fi
+	  test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+	fi
+
+	shlibpath="$finalize_shlibpath"
+	test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+	if test -n "$shlibpath"; then
+	  eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+	fi
+
+	# Get the real and link names of the library.
+	eval shared_ext=\"$shrext_cmds\"
+	eval library_names=\"$library_names_spec\"
+	set dummy $library_names
+	realname="$2"
+	shift; shift
+
+	if test -n "$soname_spec"; then
+	  eval soname=\"$soname_spec\"
+	else
+	  soname="$realname"
+	fi
+	if test -z "$dlname"; then
+	  dlname=$soname
+	fi
+
+	lib="$output_objdir/$realname"
+	for link
+	do
+	  linknames="$linknames $link"
+	done
+
+	# Use standard objects if they are pic
+	test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+
+	# Prepare the list of exported symbols
+	if test -z "$export_symbols"; then
+	  if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+	    $show "generating symbol list for \`$libname.la'"
+	    export_symbols="$output_objdir/$libname.exp"
+	    $run $rm $export_symbols
+	    cmds=$export_symbols_cmds
+	    save_ifs="$IFS"; IFS='~'
+	    for cmd in $cmds; do
+	      IFS="$save_ifs"
+	      eval cmd=\"$cmd\"
+	      if len=`expr "X$cmd" : ".*"` &&
+	       test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	        $show "$cmd"
+	        $run eval "$cmd" || exit $?
+	        skipped_export=false
+	      else
+	        # The command line is too long to execute in one step.
+	        $show "using reloadable object file for export list..."
+	        skipped_export=:
+	      fi
+	    done
+	    IFS="$save_ifs"
+	    if test -n "$export_symbols_regex"; then
+	      $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\""
+	      $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      $show "$mv \"${export_symbols}T\" \"$export_symbols\""
+	      $run eval '$mv "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+	fi
+
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"'
+	fi
+
+	tmp_deplibs=
+	for test_deplib in $deplibs; do
+		case " $convenience " in
+		*" $test_deplib "*) ;;
+		*)
+			tmp_deplibs="$tmp_deplibs $test_deplib"
+			;;
+		esac
+	done
+	deplibs="$tmp_deplibs"
+
+	if test -n "$convenience"; then
+	  if test -n "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	  else
+	    gentop="$output_objdir/${outputname}x"
+	    $show "${rm}r $gentop"
+	    $run ${rm}r "$gentop"
+	    $show "$mkdir $gentop"
+	    $run $mkdir "$gentop"
+	    status=$?
+	    if test "$status" -ne 0 && test ! -d "$gentop"; then
+	      exit $status
+	    fi
+	    generated="$generated $gentop"
+
+	    for xlib in $convenience; do
+	      # Extract the objects.
+	      case $xlib in
+	      [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+	      *) xabs=`pwd`"/$xlib" ;;
+	      esac
+	      xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+	      xdir="$gentop/$xlib"
+
+	      $show "${rm}r $xdir"
+	      $run ${rm}r "$xdir"
+	      $show "$mkdir $xdir"
+	      $run $mkdir "$xdir"
+	      status=$?
+	      if test "$status" -ne 0 && test ! -d "$xdir"; then
+		exit $status
+	      fi
+	      # We will extract separately just the conflicting names and we will no
+	      # longer touch any unique names. It is faster to leave these extract
+	      # automatically by $AR in one run.
+	      $show "(cd $xdir && $AR x $xabs)"
+	      $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+	      if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then
+		:
+	      else
+		$echo "$modename: warning: object name conflicts; renaming object files" 1>&2
+		$echo "$modename: warning: to ensure that they will not overwrite" 1>&2
+		$AR t "$xabs" | sort | uniq -cd | while read -r count name
+		do
+		  i=1
+		  while test "$i" -le "$count"
+		  do
+		   # Put our $i before any first dot (extension)
+		   # Never overwrite any file
+		   name_to="$name"
+		   while test "X$name_to" = "X$name" || test -f "$xdir/$name_to"
+		   do
+		     name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"`
+		   done
+		   $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')"
+		   $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $?
+		   i=`expr $i + 1`
+		  done
+		done
+	      fi
+
+	      libobjs="$libobjs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+	    done
+	  fi
+	fi
+
+	if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+	  eval flag=\"$thread_safe_flag_spec\"
+	  linker_flags="$linker_flags $flag"
+	fi
+
+	# Make a backup of the uninstalled library when relinking
+	if test "$mode" = relink; then
+	  $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $?
+	fi
+
+	# Do each of the archive commands.
+	if test "$module" = yes && test -n "$module_cmds" ; then
+	  if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	    eval test_cmds=\"$module_expsym_cmds\"
+	    cmds=$module_expsym_cmds
+	  else
+	    eval test_cmds=\"$module_cmds\"
+	    cmds=$module_cmds
+	  fi
+	else
+	if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	  eval test_cmds=\"$archive_expsym_cmds\"
+	  cmds=$archive_expsym_cmds
+	else
+	  eval test_cmds=\"$archive_cmds\"
+	  cmds=$archive_cmds
+	  fi
+	fi
+
+	if test "X$skipped_export" != "X:" && len=`expr "X$test_cmds" : ".*"` &&
+	   test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  :
+	else
+	  # The command line is too long to link in one step, link piecewise.
+	  $echo "creating reloadable object files..."
+
+	  # Save the value of $output and $libobjs because we want to
+	  # use them later.  If we have whole_archive_flag_spec, we
+	  # want to use save_libobjs as it was before
+	  # whole_archive_flag_spec was expanded, because we can't
+	  # assume the linker understands whole_archive_flag_spec.
+	  # This may have to be revisited, in case too many
+	  # convenience libraries get linked in and end up exceeding
+	  # the spec.
+	  if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	  fi
+	  save_output=$output
+
+	  # Clear the reloadable object creation command queue and
+	  # initialize k to one.
+	  test_cmds=
+	  concat_cmds=
+	  objlist=
+	  delfiles=
+	  last_robj=
+	  k=1
+	  output=$output_objdir/$save_output-${k}.$objext
+	  # Loop over the list of objects to be linked.
+	  for obj in $save_libobjs
+	  do
+	    eval test_cmds=\"$reload_cmds $objlist $last_robj\"
+	    if test "X$objlist" = X ||
+	       { len=`expr "X$test_cmds" : ".*"` &&
+		 test "$len" -le "$max_cmd_len"; }; then
+	      objlist="$objlist $obj"
+	    else
+	      # The command $test_cmds is almost too long, add a
+	      # command to the queue.
+	      if test "$k" -eq 1 ; then
+		# The first file doesn't have a previous command to add.
+		eval concat_cmds=\"$reload_cmds $objlist $last_robj\"
+	      else
+		# All subsequent reloadable object files will link in
+		# the last one created.
+		eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\"
+	      fi
+	      last_robj=$output_objdir/$save_output-${k}.$objext
+	      k=`expr $k + 1`
+	      output=$output_objdir/$save_output-${k}.$objext
+	      objlist=$obj
+	      len=1
+	    fi
+	  done
+	  # Handle the remaining objects by creating one last
+	  # reloadable object file.  All subsequent reloadable object
+	  # files will link in the last one created.
+	  test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	  eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\"
+
+	  if ${skipped_export-false}; then
+	    $show "generating symbol list for \`$libname.la'"
+	    export_symbols="$output_objdir/$libname.exp"
+	    $run $rm $export_symbols
+	    libobjs=$output
+	    # Append the command to create the export file.
+	    eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\"
+          fi
+
+	  # Set up a command to remove the reloadale object files
+	  # after they are used.
+	  i=0
+	  while test "$i" -lt "$k"
+	  do
+	    i=`expr $i + 1`
+	    delfiles="$delfiles $output_objdir/$save_output-${i}.$objext"
+	  done
+
+	  $echo "creating a temporary reloadable object file: $output"
+
+	  # Loop through the commands generated above and execute them.
+	  save_ifs="$IFS"; IFS='~'
+	  for cmd in $concat_cmds; do
+	    IFS="$save_ifs"
+	    $show "$cmd"
+	    $run eval "$cmd" || exit $?
+	  done
+	  IFS="$save_ifs"
+
+	  libobjs=$output
+	  # Restore the value of output.
+	  output=$save_output
+
+	  if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	  fi
+	  # Expand the library linking commands again to reset the
+	  # value of $libobjs for piecewise linking.
+
+	  # Do each of the archive commands.
+	  if test "$module" = yes && test -n "$module_cmds" ; then
+	    if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	      cmds=$module_expsym_cmds
+	    else
+	      cmds=$module_cmds
+	    fi
+	  else
+	  if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	    cmds=$archive_expsym_cmds
+	  else
+	    cmds=$archive_cmds
+	    fi
+	  fi
+
+	  # Append the command to remove the reloadable object files
+	  # to the just-reset $cmds.
+	  eval cmds=\"\$cmds~\$rm $delfiles\"
+	fi
+	save_ifs="$IFS"; IFS='~'
+	for cmd in $cmds; do
+	  IFS="$save_ifs"
+	  eval cmd=\"$cmd\"
+	  $show "$cmd"
+	  $run eval "$cmd" || exit $?
+	done
+	IFS="$save_ifs"
+
+	# Restore the uninstalled library and exit
+	if test "$mode" = relink; then
+	  $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $?
+	  exit $EXIT_SUCCESS
+	fi
+
+	# Create links to the real library.
+	for linkname in $linknames; do
+	  if test "$realname" != "$linkname"; then
+	    $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)"
+	    $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $?
+	  fi
+	done
+
+	# If -module or -export-dynamic was specified, set the dlname.
+	if test "$module" = yes || test "$export_dynamic" = yes; then
+	  # On all known operating systems, these are identical.
+	  dlname="$soname"
+	fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$deplibs"; then
+	$echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	$echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+	$echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+	$echo "$modename: warning: \`-R' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+	$echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$release"; then
+	$echo "$modename: warning: \`-release' is ignored for objects" 1>&2
+      fi
+
+      case $output in
+      *.lo)
+	if test -n "$objs$old_deplibs"; then
+	  $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+	libobj="$output"
+	obj=`$echo "X$output" | $Xsed -e "$lo2o"`
+	;;
+      *)
+	libobj=
+	obj="$output"
+	;;
+      esac
+
+      # Delete the old objects.
+      $run $rm $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec
+      wl=
+
+      if test -n "$convenience"; then
+	if test -n "$whole_archive_flag_spec"; then
+	  eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\"
+	else
+	  gentop="$output_objdir/${obj}x"
+	  $show "${rm}r $gentop"
+	  $run ${rm}r "$gentop"
+	  $show "$mkdir $gentop"
+	  $run $mkdir "$gentop"
+	  status=$?
+	  if test "$status" -ne 0 && test ! -d "$gentop"; then
+	    exit $status
+	  fi
+	  generated="$generated $gentop"
+
+	  for xlib in $convenience; do
+	    # Extract the objects.
+	    case $xlib in
+	    [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+	    *) xabs=`pwd`"/$xlib" ;;
+	    esac
+	    xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+	    xdir="$gentop/$xlib"
+
+	    $show "${rm}r $xdir"
+	    $run ${rm}r "$xdir"
+	    $show "$mkdir $xdir"
+	    $run $mkdir "$xdir"
+	    status=$?
+	    if test "$status" -ne 0 && test ! -d "$xdir"; then
+	      exit $status
+	    fi
+	    # We will extract separately just the conflicting names and we will no
+	    # longer touch any unique names. It is faster to leave these extract
+	    # automatically by $AR in one run.
+	    $show "(cd $xdir && $AR x $xabs)"
+	    $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+	    if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then
+	      :
+	    else
+	      $echo "$modename: warning: object name conflicts; renaming object files" 1>&2
+	      $echo "$modename: warning: to ensure that they will not overwrite" 1>&2
+	      $AR t "$xabs" | sort | uniq -cd | while read -r count name
+	      do
+		i=1
+		while test "$i" -le "$count"
+		do
+		 # Put our $i before any first dot (extension)
+		 # Never overwrite any file
+		 name_to="$name"
+		 while test "X$name_to" = "X$name" || test -f "$xdir/$name_to"
+		 do
+		   name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"`
+		 done
+		 $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')"
+		 $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $?
+		 i=`expr $i + 1`
+		done
+	      done
+	    fi
+
+	    reload_conv_objs="$reload_objs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+	  done
+	fi
+      fi
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      cmds=$reload_cmds
+      save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+	IFS="$save_ifs"
+	eval cmd=\"$cmd\"
+	$show "$cmd"
+	$run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+	if test -n "$gentop"; then
+	  $show "${rm}r $gentop"
+	  $run ${rm}r $gentop
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+	if test -n "$gentop"; then
+	  $show "${rm}r $gentop"
+	  $run ${rm}r $gentop
+	fi
+
+	# Create an invalid libtool object if no PIC, so that we don't
+	# accidentally link it into a program.
+	# $show "echo timestamp > $libobj"
+	# $run eval "echo timestamp > $libobj" || exit $?
+	exit $EXIT_SUCCESS
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+	# Only do commands if we really have different PIC objects.
+	reload_objs="$libobjs $reload_conv_objs"
+	output="$libobj"
+	cmds=$reload_cmds
+	save_ifs="$IFS"; IFS='~'
+	for cmd in $cmds; do
+	  IFS="$save_ifs"
+	  eval cmd=\"$cmd\"
+	  $show "$cmd"
+	  $run eval "$cmd" || exit $?
+	done
+	IFS="$save_ifs"
+      fi
+
+      if test -n "$gentop"; then
+	$show "${rm}r $gentop"
+	$run ${rm}r $gentop
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+	*cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;;
+      esac
+      if test -n "$vinfo"; then
+	$echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2
+      fi
+
+      if test -n "$release"; then
+	$echo "$modename: warning: \`-release' is ignored for programs" 1>&2
+      fi
+
+      if test "$preload" = yes; then
+	if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown &&
+	   test "$dlopen_self_static" = unknown; then
+	  $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support."
+	fi
+      fi
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+	# On Rhapsody replace the C library is the System framework
+	compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'`
+	finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'`
+	;;
+      esac
+
+      case $host in
+      *darwin*)
+        # Don't allow lazy linking, it breaks C++ global constructors
+        if test "$tagname" = CXX ; then
+        compile_command="$compile_command ${wl}-bind_at_load"
+        finalize_command="$finalize_command ${wl}-bind_at_load"
+        fi
+        ;;
+      esac
+
+      compile_command="$compile_command $compile_deplibs"
+      finalize_command="$finalize_command $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	for libdir in $rpath $xrpath; do
+	  # This is the magic to use -rpath.
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) finalize_rpath="$finalize_rpath $libdir" ;;
+	  esac
+	done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    rpath="$rpath $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) perm_rpath="$perm_rpath $libdir" ;;
+	  esac
+	fi
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+	  case :$dllsearchpath: in
+	  *":$libdir:"*) ;;
+	  *) dllsearchpath="$dllsearchpath:$libdir";;
+	  esac
+	  ;;
+	esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    rpath="$rpath $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$finalize_perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+	  esac
+	fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+	# Transform all the library objects into standard objects.
+	compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+	finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+      fi
+
+      dlsyms=
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	if test -n "$NM" && test -n "$global_symbol_pipe"; then
+	  dlsyms="${outputname}S.c"
+	else
+	  $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2
+	fi
+      fi
+
+      if test -n "$dlsyms"; then
+	case $dlsyms in
+	"") ;;
+	*.c)
+	  # Discover the nlist of each of the dlfiles.
+	  nlist="$output_objdir/${outputname}.nm"
+
+	  $show "$rm $nlist ${nlist}S ${nlist}T"
+	  $run $rm "$nlist" "${nlist}S" "${nlist}T"
+
+	  # Parse the name list into a source file.
+	  $show "creating $output_objdir/$dlsyms"
+
+	  test -z "$run" && $echo > "$output_objdir/$dlsyms" "\
+/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */
+/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* Prevent the only kind of declaration conflicts we can make. */
+#define lt_preloaded_symbols some_other_symbol
+
+/* External symbol declarations for the compiler. */\
+"
+
+	  if test "$dlself" = yes; then
+	    $show "generating symbol list for \`$output'"
+
+	    test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist"
+
+	    # Add our own program objects to the symbol list.
+	    progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+	    for arg in $progfiles; do
+	      $show "extracting global C symbols from \`$arg'"
+	      $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+	    done
+
+	    if test -n "$exclude_expsyms"; then
+	      $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+	      $run eval '$mv "$nlist"T "$nlist"'
+	    fi
+
+	    if test -n "$export_symbols_regex"; then
+	      $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+	      $run eval '$mv "$nlist"T "$nlist"'
+	    fi
+
+	    # Prepare the list of exported symbols
+	    if test -z "$export_symbols"; then
+	      export_symbols="$output_objdir/$output.exp"
+	      $run $rm $export_symbols
+	      $run eval "${SED} -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+	    else
+	      $run eval "${SED} -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"'
+	      $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T'
+	      $run eval 'mv "$nlist"T "$nlist"'
+	    fi
+	  fi
+
+	  for arg in $dlprefiles; do
+	    $show "extracting global C symbols from \`$arg'"
+	    name=`$echo "$arg" | ${SED} -e 's%^.*/%%'`
+	    $run eval '$echo ": $name " >> "$nlist"'
+	    $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+	  done
+
+	  if test -z "$run"; then
+	    # Make sure we have at least an empty file.
+	    test -f "$nlist" || : > "$nlist"
+
+	    if test -n "$exclude_expsyms"; then
+	      $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+	      $mv "$nlist"T "$nlist"
+	    fi
+
+	    # Try sorting and uniquifying the output.
+	    if grep -v "^: " < "$nlist" |
+		if sort -k 3 </dev/null >/dev/null 2>&1; then
+		  sort -k 3
+		else
+		  sort +2
+		fi |
+		uniq > "$nlist"S; then
+	      :
+	    else
+	      grep -v "^: " < "$nlist" > "$nlist"S
+	    fi
+
+	    if test -f "$nlist"S; then
+	      eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"'
+	    else
+	      $echo '/* NONE */' >> "$output_objdir/$dlsyms"
+	    fi
+
+	    $echo >> "$output_objdir/$dlsyms" "\
+
+#undef lt_preloaded_symbols
+
+#if defined (__STDC__) && __STDC__
+# define lt_ptr void *
+#else
+# define lt_ptr char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr address;
+}
+lt_preloaded_symbols[] =
+{\
+"
+
+	    eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms"
+
+	    $echo >> "$output_objdir/$dlsyms" "\
+  {0, (lt_ptr) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+	  fi
+
+	  pic_flag_for_symtable=
+	  case $host in
+	  # compiling the symbol table file with pic_flag works around
+	  # a FreeBSD bug that causes programs to crash when -lm is
+	  # linked before any other PIC object.  But we must not use
+	  # pic_flag when linking with -static.  The problem exists in
+	  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+	  *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+	    case "$compile_command " in
+	    *" -static "*) ;;
+	    *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";;
+	    esac;;
+	  *-*-hpux*)
+	    case "$compile_command " in
+	    *" -static "*) ;;
+	    *) pic_flag_for_symtable=" $pic_flag";;
+	    esac
+	  esac
+
+	  # Now compile the dynamic symbol file.
+	  $show "(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")"
+	  $run eval '(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $?
+
+	  # Clean up the generated files.
+	  $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T"
+	  $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T"
+
+	  # Transform the symbol file into the correct name.
+	  compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+	  finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+	  ;;
+	*)
+	  $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+      else
+	# We keep going just in case the user didn't refer to
+	# lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+	# really was required.
+
+	# Nullify the symbol file.
+	compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+	finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+      fi
+
+      if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+	# Replace the output file specification.
+	compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+	link_command="$compile_command$compile_rpath"
+
+	# We have no uninstalled library dependencies, so finalize right now.
+	$show "$link_command"
+	$run eval "$link_command"
+	status=$?
+
+	# Delete the generated files.
+	if test -n "$dlsyms"; then
+	  $show "$rm $output_objdir/${outputname}S.${objext}"
+	  $run $rm "$output_objdir/${outputname}S.${objext}"
+	fi
+
+	exit $status
+      fi
+
+      if test -n "$shlibpath_var"; then
+	# We should set the shlibpath_var
+	rpath=
+	for dir in $temp_rpath; do
+	  case $dir in
+	  [\\/]* | [A-Za-z]:[\\/]*)
+	    # Absolute path.
+	    rpath="$rpath$dir:"
+	    ;;
+	  *)
+	    # Relative path: add a thisdir entry.
+	    rpath="$rpath\$thisdir/$dir:"
+	    ;;
+	  esac
+	done
+	temp_rpath="$rpath"
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+	compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+	finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+	if test -n "$perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $perm_rpath; do
+	    rpath="$rpath$dir:"
+	  done
+	  compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+	if test -n "$finalize_perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $finalize_perm_rpath; do
+	    rpath="$rpath$dir:"
+	  done
+	  finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+      fi
+
+      if test "$no_install" = yes; then
+	# We don't need to create a wrapper script.
+	link_command="$compile_var$compile_command$compile_rpath"
+	# Replace the output file specification.
+	link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+	# Delete the old output file.
+	$run $rm $output
+	# Link the executable and exit
+	$show "$link_command"
+	$run eval "$link_command" || exit $?
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$hardcode_action" = relink; then
+	# Fast installation is not supported
+	link_command="$compile_var$compile_command$compile_rpath"
+	relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+	$echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2
+	$echo "$modename: \`$output' will be relinked during installation" 1>&2
+      else
+	if test "$fast_install" != no; then
+	  link_command="$finalize_var$compile_command$finalize_rpath"
+	  if test "$fast_install" = yes; then
+	    relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+	  else
+	    # fast_install is set to needless
+	    relink_command=
+	  fi
+	else
+	  link_command="$compile_var$compile_command$compile_rpath"
+	  relink_command="$finalize_var$finalize_command$finalize_rpath"
+	fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      $show "$link_command"
+      $run eval "$link_command" || exit $?
+
+      # Now create the wrapper script.
+      $show "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+	# Preserve any variables that may affect compiler behavior
+	for var in $variables_saved_for_relink; do
+	  if eval test -z \"\${$var+set}\"; then
+	    relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command"
+	  elif eval var_value=\$$var; test -z "$var_value"; then
+	    relink_command="$var=; export $var; $relink_command"
+	  else
+	    var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"`
+	    relink_command="$var=\"$var_value\"; export $var; $relink_command"
+	  fi
+	done
+	relink_command="(cd `pwd`; $relink_command)"
+	relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Quote $echo for shipping.
+      if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then
+	case $progpath in
+	[\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";;
+	*) qecho="$SHELL `pwd`/$progpath --fallback-echo";;
+	esac
+	qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"`
+      else
+	qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if our run command is non-null.
+      if test -z "$run"; then
+	# win32 will think the script is a binary if it has
+	# a .exe suffix, so we strip it off here.
+	case $output in
+	  *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;;
+	esac
+	# test for cygwin because mv fails w/o .exe extensions
+	case $host in
+	  *cygwin*)
+	    exeext=.exe
+	    outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;;
+	  *) exeext= ;;
+	esac
+	case $host in
+	  *cygwin* | *mingw* )
+	    cwrappersource=`$echo ${objdir}/lt-${output}.c`
+	    cwrapper=`$echo ${output}.exe`
+	    $rm $cwrappersource $cwrapper
+	    trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+	    cat > $cwrappersource <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+
+   Currently, it simply execs the wrapper *script* "/bin/sh $output",
+   but could eventually absorb all of the scripts functionality and
+   exec $objdir/$outputname directly.
+*/
+EOF
+	    cat >> $cwrappersource<<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef DIR_SEPARATOR
+#define DIR_SEPARATOR '/'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+#define HAVE_DOS_BASED_FILE_SYSTEM
+#ifndef DIR_SEPARATOR_2
+#define DIR_SEPARATOR_2 '\\'
+#endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+        (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+const char *program_name = NULL;
+
+void * xmalloc (size_t num);
+char * xstrdup (const char *string);
+char * basename (const char *name);
+char * fnqualify(const char *path);
+char * strendzap(char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int i;
+
+  program_name = (char *) xstrdup ((char *) basename (argv[0]));
+  newargz = XMALLOC(char *, argc+2);
+EOF
+
+	    cat >> $cwrappersource <<EOF
+  newargz[0] = "$SHELL";
+EOF
+
+	    cat >> $cwrappersource <<"EOF"
+  newargz[1] = fnqualify(argv[0]);
+  /* we know the script has the same name, without the .exe */
+  /* so make sure newargz[1] doesn't end in .exe */
+  strendzap(newargz[1],".exe");
+  for (i = 1; i < argc; i++)
+    newargz[i+1] = xstrdup(argv[i]);
+  newargz[argc+1] = NULL;
+EOF
+
+	    cat >> $cwrappersource <<EOF
+  execv("$SHELL",newargz);
+EOF
+
+	    cat >> $cwrappersource <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void * p = (void *) malloc (num);
+  if (!p)
+    lt_fatal ("Memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL
+;
+}
+
+char *
+basename (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha (name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return (char *) base;
+}
+
+char *
+fnqualify(const char *path)
+{
+  size_t size;
+  char *p;
+  char tmp[LT_PATHMAX + 1];
+
+  assert(path != NULL);
+
+  /* Is it qualified already? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha (path[0]) && path[1] == ':')
+    return xstrdup (path);
+#endif
+  if (IS_DIR_SEPARATOR (path[0]))
+    return xstrdup (path);
+
+  /* prepend the current directory */
+  /* doesn't handle '~' */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal ("getcwd failed");
+  size = strlen(tmp) + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */
+  p = XMALLOC(char, size);
+  sprintf(p, "%s%c%s", tmp, DIR_SEPARATOR, path);
+  return p;
+}
+
+char *
+strendzap(char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert(str != NULL);
+  assert(pat != NULL);
+
+  len = strlen(str);
+  patlen = strlen(pat);
+
+  if (patlen <= len)
+  {
+    str += len - patlen;
+    if (strcmp(str, pat) == 0)
+      *str = '\0';
+  }
+  return str;
+}
+
+static void
+lt_error_core (int exit_status, const char * mode,
+          const char * message, va_list ap)
+{
+  fprintf (stderr, "%s: %s: ", program_name, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+  va_end (ap);
+}
+EOF
+	  # we should really use a build-platform specific compiler
+	  # here, but OTOH, the wrappers (shell script and this C one)
+	  # are only useful if you want to execute the "real" binary.
+	  # Since the "real" binary is built for $host, then this
+	  # wrapper might as well be built for $host, too.
+	  $run $LTCC -s -o $cwrapper $cwrappersource
+	  ;;
+	esac
+	$rm $output
+	trap "$rm $output; exit $EXIT_FAILURE" 1 2 15
+
+	$echo > $output "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='${SED} -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test \"\${CDPATH+set}\" = set; then CDPATH=:; export CDPATH; fi
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variable:
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$echo are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    echo=\"$qecho\"
+    file=\"\$0\"
+    # Make sure echo works.
+    if test \"X\$1\" = X--no-reexec; then
+      # Discard the --no-reexec flag, and continue.
+      shift
+    elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then
+      # Yippee, \$echo works!
+      :
+    else
+      # Restart under the correct shell, and then maybe \$echo will work.
+      exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+    fi
+  fi\
+"
+	$echo >> $output "\
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
+  done
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+	if test "$fast_install" = yes; then
+	  $echo >> $output "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" || \\
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $mkdir \"\$progdir\"
+    else
+      $rm \"\$progdir/\$file\"
+    fi"
+
+	  $echo >> $output "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+	$echo \"\$relink_command_output\" >&2
+	$rm \"\$progdir/\$file\"
+	exit $EXIT_FAILURE
+      fi
+    fi
+
+    $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $rm \"\$progdir/\$program\";
+      $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $rm \"\$progdir/\$file\"
+  fi"
+	else
+	  $echo >> $output "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+	fi
+
+	$echo >> $output "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+	# Export our shlibpath_var if we have one.
+	if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+	  $echo >> $output "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+	fi
+
+	# fixup the dll searchpath if we need to.
+	if test -n "$dllsearchpath"; then
+	  $echo >> $output "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+	fi
+
+	$echo >> $output "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+"
+	case $host in
+	# Backslashes separate directories on plain windows
+	*-*-mingw | *-*-os2*)
+	  $echo >> $output "\
+      exec \$progdir\\\\\$program \${1+\"\$@\"}
+"
+	  ;;
+
+	*)
+	  $echo >> $output "\
+      exec \$progdir/\$program \${1+\"\$@\"}
+"
+	  ;;
+	esac
+	$echo >> $output "\
+      \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\"
+      exit $EXIT_FAILURE
+    fi
+  else
+    # The program doesn't exist.
+    \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2
+    \$echo \"This script is just a wrapper for \$program.\" 1>&2
+    $echo \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit $EXIT_FAILURE
+  fi
+fi\
+"
+	chmod +x $output
+      fi
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+	oldobjs="$libobjs_save"
+	addlibs="$convenience"
+	build_libtool_libs=no
+      else
+	if test "$build_libtool_libs" = module; then
+	  oldobjs="$libobjs_save"
+	  build_libtool_libs=no
+	else
+	  oldobjs="$old_deplibs $non_pic_objects"
+	fi
+	addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+	gentop="$output_objdir/${outputname}x"
+	$show "${rm}r $gentop"
+	$run ${rm}r "$gentop"
+	$show "$mkdir $gentop"
+	$run $mkdir "$gentop"
+	status=$?
+	if test "$status" -ne 0 && test ! -d "$gentop"; then
+	  exit $status
+	fi
+	generated="$generated $gentop"
+
+	# Add in members from convenience archives.
+	for xlib in $addlibs; do
+	  # Extract the objects.
+	  case $xlib in
+	  [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+	  *) xabs=`pwd`"/$xlib" ;;
+	  esac
+	  xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+	  xdir="$gentop/$xlib"
+
+	  $show "${rm}r $xdir"
+	  $run ${rm}r "$xdir"
+	  $show "$mkdir $xdir"
+	  $run $mkdir "$xdir"
+	  status=$?
+	  if test "$status" -ne 0 && test ! -d "$xdir"; then
+	    exit $status
+	  fi
+	  # We will extract separately just the conflicting names and we will no
+	  # longer touch any unique names. It is faster to leave these extract
+	  # automatically by $AR in one run.
+	  $show "(cd $xdir && $AR x $xabs)"
+	  $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+	  if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then
+	    :
+	  else
+	    $echo "$modename: warning: object name conflicts; renaming object files" 1>&2
+	    $echo "$modename: warning: to ensure that they will not overwrite" 1>&2
+	    $AR t "$xabs" | sort | uniq -cd | while read -r count name
+	    do
+	      i=1
+	      while test "$i" -le "$count"
+	      do
+	       # Put our $i before any first dot (extension)
+	       # Never overwrite any file
+	       name_to="$name"
+	       while test "X$name_to" = "X$name" || test -f "$xdir/$name_to"
+	       do
+		 name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"`
+	       done
+	       $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')"
+	       $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $?
+	       i=`expr $i + 1`
+	      done
+	    done
+	  fi
+
+	  oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP`
+	done
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+       cmds=$old_archive_from_new_cmds
+      else
+	eval cmds=\"$old_archive_cmds\"
+
+	if len=`expr "X$cmds" : ".*"` &&
+	     test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  cmds=$old_archive_cmds
+	else
+	  # the command line is too long to link in one step, link in parts
+	  $echo "using piecewise archive linking..."
+	  save_RANLIB=$RANLIB
+	  RANLIB=:
+	  objlist=
+	  concat_cmds=
+	  save_oldobjs=$oldobjs
+	  # GNU ar 2.10+ was changed to match POSIX; thus no paths are
+	  # encoded into archives.  This makes 'ar r' malfunction in
+	  # this piecewise linking case whenever conflicting object
+	  # names appear in distinct ar calls; check, warn and compensate.
+	    if (for obj in $save_oldobjs
+	    do
+	      $echo "X$obj" | $Xsed -e 's%^.*/%%'
+	    done | sort | sort -uc >/dev/null 2>&1); then
+	    :
+	  else
+	    $echo "$modename: warning: object name conflicts; overriding AR_FLAGS to 'cq'" 1>&2
+	    $echo "$modename: warning: to ensure that POSIX-compatible ar will work" 1>&2
+	    AR_FLAGS=cq
+	  fi
+	  # Is there a better way of finding the last object in the list?
+	  for obj in $save_oldobjs
+	  do
+	    last_oldobj=$obj
+	  done
+	  for obj in $save_oldobjs
+	  do
+	    oldobjs="$objlist $obj"
+	    objlist="$objlist $obj"
+	    eval test_cmds=\"$old_archive_cmds\"
+	    if len=`expr "X$test_cmds" : ".*"` &&
+	       test "$len" -le "$max_cmd_len"; then
+	      :
+	    else
+	      # the above command should be used before it gets too long
+	      oldobjs=$objlist
+	      if test "$obj" = "$last_oldobj" ; then
+	        RANLIB=$save_RANLIB
+	      fi
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+	      objlist=
+	    fi
+	  done
+	  RANLIB=$save_RANLIB
+	  oldobjs=$objlist
+	  if test "X$oldobjs" = "X" ; then
+	    eval cmds=\"\$concat_cmds\"
+	  else
+	    eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+	  fi
+	fi
+      fi
+      save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+        eval cmd=\"$cmd\"
+	IFS="$save_ifs"
+	$show "$cmd"
+	$run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$generated"; then
+      $show "${rm}r$generated"
+      $run ${rm}r$generated
+    fi
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      $show "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+	if eval test -z \"\${$var+set}\"; then
+	  relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command"
+	elif eval var_value=\$$var; test -z "$var_value"; then
+	  relink_command="$var=; export $var; $relink_command"
+	else
+	  var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"`
+	  relink_command="$var=\"$var_value\"; export $var; $relink_command"
+	fi
+      done
+      # Quote the link command for shipping.
+      relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      if test "$hardcode_automatic" = yes ; then
+	relink_command=
+      fi
+
+
+      # Only create the output if not a dry run.
+      if test -z "$run"; then
+	for installed in no yes; do
+	  if test "$installed" = yes; then
+	    if test -z "$install_libdir"; then
+	      break
+	    fi
+	    output="$output_objdir/$outputname"i
+	    # Replace all uninstalled libtool libraries with the installed ones
+	    newdependency_libs=
+	    for deplib in $dependency_libs; do
+	      case $deplib in
+	      *.la)
+		name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'`
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+		if test -z "$libdir"; then
+		  $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
+		  exit $EXIT_FAILURE
+		fi
+		newdependency_libs="$newdependency_libs $libdir/$name"
+		;;
+	      *) newdependency_libs="$newdependency_libs $deplib" ;;
+	      esac
+	    done
+	    dependency_libs="$newdependency_libs"
+	    newdlfiles=
+	    for lib in $dlfiles; do
+	      name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+	      eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+	      if test -z "$libdir"; then
+		$echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+		exit $EXIT_FAILURE
+	      fi
+	      newdlfiles="$newdlfiles $libdir/$name"
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+	      eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+	      if test -z "$libdir"; then
+		$echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+		exit $EXIT_FAILURE
+	      fi
+	      newdlprefiles="$newdlprefiles $libdir/$name"
+	    done
+	    dlprefiles="$newdlprefiles"
+	  else
+	    newdlfiles=
+	    for lib in $dlfiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      newdlfiles="$newdlfiles $abs"
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      newdlprefiles="$newdlprefiles $abs"
+	    done
+	    dlprefiles="$newdlprefiles"
+	  fi
+	  $rm $output
+	  # place dlname in correct position for cygwin
+	  tdlname=$dlname
+	  case $host,$output,$installed,$module,$dlname in
+	    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+	  esac
+	  $echo > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+	  if test "$installed" = no && test "$need_relink" = yes; then
+	    $echo >> $output "\
+relink_command=\"$relink_command\""
+	  fi
+	done
+      fi
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)"
+      $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $?
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+    ;;
+
+  # libtool install mode
+  install)
+    modename="$modename: install"
+
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       $echo "X$nonopt" | $Xsed | grep shtool > /dev/null; then
+      # Aesthetically quote it.
+      arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"`
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*)
+	arg="\"$arg\""
+	;;
+      esac
+      install_prog="$arg "
+      arg="$1"
+      shift
+    else
+      install_prog=
+      arg="$nonopt"
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+    case $arg in
+    *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*)
+      arg="\"$arg\""
+      ;;
+    esac
+    install_prog="$install_prog$arg"
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    for arg
+    do
+      if test -n "$dest"; then
+	files="$files $dest"
+	dest="$arg"
+	continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f) prev="-f" ;;
+      -g) prev="-g" ;;
+      -m) prev="-m" ;;
+      -o) prev="-o" ;;
+      -s)
+	stripme=" -s"
+	continue
+	;;
+      -*) ;;
+
+      *)
+	# If the previous option needed an argument, then skip it.
+	if test -n "$prev"; then
+	  prev=
+	else
+	  dest="$arg"
+	  continue
+	fi
+	;;
+      esac
+
+      # Aesthetically quote the argument.
+      arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*)
+	arg="\"$arg\""
+	;;
+      esac
+      install_prog="$install_prog $arg"
+    done
+
+    if test -z "$install_prog"; then
+      $echo "$modename: you must specify an install program" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prev' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+	$echo "$modename: no file or destination specified" 1>&2
+      else
+	$echo "$modename: you must specify a destination" 1>&2
+      fi
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    # Strip any trailing slash from the destination.
+    dest=`$echo "X$dest" | $Xsed -e 's%/$%%'`
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'`
+      test "X$destdir" = "X$dest" && destdir=.
+      destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'`
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files
+      if test "$#" -gt 2; then
+	$echo "$modename: \`$dest' is not a directory" 1>&2
+	$echo "$help" 1>&2
+	exit $EXIT_FAILURE
+      fi
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+	case $file in
+	*.lo) ;;
+	*)
+	  $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+	# Do the static libraries later.
+	staticlibs="$staticlibs $file"
+	;;
+
+      *.la)
+	# Check to see that this really is a libtool archive.
+	if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+	else
+	  $echo "$modename: \`$file' is not a valid libtool archive" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	library_names=
+	old_library=
+	relink_command=
+	# If there is no directory component, then add one.
+	case $file in
+	*/* | *\\*) . $file ;;
+	*) . ./$file ;;
+	esac
+
+	# Add the libdir to current_libdirs if it is the destination.
+	if test "X$destdir" = "X$libdir"; then
+	  case "$current_libdirs " in
+	  *" $libdir "*) ;;
+	  *) current_libdirs="$current_libdirs $libdir" ;;
+	  esac
+	else
+	  # Note the libdir as a future libdir.
+	  case "$future_libdirs " in
+	  *" $libdir "*) ;;
+	  *) future_libdirs="$future_libdirs $libdir" ;;
+	  esac
+	fi
+
+	dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/
+	test "X$dir" = "X$file/" && dir=
+	dir="$dir$objdir"
+
+	if test -n "$relink_command"; then
+	  # Determine the prefix the user has applied to our future dir.
+	  inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"`
+
+	  # Don't allow the user to place us outside of our expected
+	  # location b/c this prevents finding dependent libraries that
+	  # are installed to the same prefix.
+	  # At present, this check doesn't affect windows .dll's that
+	  # are installed into $libdir/../bin (currently, that works fine)
+	  # but it's something to keep an eye on.
+	  if test "$inst_prefix_dir" = "$destdir"; then
+	    $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+
+	  if test -n "$inst_prefix_dir"; then
+	    # Stick the inst_prefix_dir data into the link command.
+	    relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+	  else
+	    relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+	  fi
+
+	  $echo "$modename: warning: relinking \`$file'" 1>&2
+	  $show "$relink_command"
+	  if $run eval "$relink_command"; then :
+	  else
+	    $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+	fi
+
+	# See the names of the shared library.
+	set dummy $library_names
+	if test -n "$2"; then
+	  realname="$2"
+	  shift
+	  shift
+
+	  srcname="$realname"
+	  test -n "$relink_command" && srcname="$realname"T
+
+	  # Install the shared library and build the symlinks.
+	  $show "$install_prog $dir/$srcname $destdir/$realname"
+	  $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $?
+	  if test -n "$stripme" && test -n "$striplib"; then
+	    $show "$striplib $destdir/$realname"
+	    $run eval "$striplib $destdir/$realname" || exit $?
+	  fi
+
+	  if test "$#" -gt 0; then
+	    # Delete the old symlinks, and create new ones.
+	    for linkname
+	    do
+	      if test "$linkname" != "$realname"; then
+		$show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+		$run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+	      fi
+	    done
+	  fi
+
+	  # Do each command in the postinstall commands.
+	  lib="$destdir/$realname"
+	  cmds=$postinstall_cmds
+	  save_ifs="$IFS"; IFS='~'
+	  for cmd in $cmds; do
+	    IFS="$save_ifs"
+	    eval cmd=\"$cmd\"
+	    $show "$cmd"
+	    $run eval "$cmd" || exit $?
+	  done
+	  IFS="$save_ifs"
+	fi
+
+	# Install the pseudo-library for information purposes.
+	name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+	instname="$dir/$name"i
+	$show "$install_prog $instname $destdir/$name"
+	$run eval "$install_prog $instname $destdir/$name" || exit $?
+
+	# Maybe install the static library, too.
+	test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+	;;
+
+      *.lo)
+	# Install (i.e. copy) a libtool object.
+
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+	  destfile="$destdir/$destfile"
+	fi
+
+	# Deduce the name of the destination old-style object file.
+	case $destfile in
+	*.lo)
+	  staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"`
+	  ;;
+	*.$objext)
+	  staticdest="$destfile"
+	  destfile=
+	  ;;
+	*)
+	  $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	  ;;
+	esac
+
+	# Install the libtool object if requested.
+	if test -n "$destfile"; then
+	  $show "$install_prog $file $destfile"
+	  $run eval "$install_prog $file $destfile" || exit $?
+	fi
+
+	# Install the old object if enabled.
+	if test "$build_old_libs" = yes; then
+	  # Deduce the name of the old-style object file.
+	  staticobj=`$echo "X$file" | $Xsed -e "$lo2o"`
+
+	  $show "$install_prog $staticobj $staticdest"
+	  $run eval "$install_prog \$staticobj \$staticdest" || exit $?
+	fi
+	exit $EXIT_SUCCESS
+	;;
+
+      *)
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+	  destfile="$destdir/$destfile"
+	fi
+
+	# If the file is missing, and there is a .exe on the end, strip it
+	# because it is most likely a libtool script we actually want to
+	# install
+	stripped_ext=""
+	case $file in
+	  *.exe)
+	    if test ! -f "$file"; then
+	      file=`$echo $file|${SED} 's,.exe$,,'`
+	      stripped_ext=".exe"
+	    fi
+	    ;;
+	esac
+
+	# Do a test to see if this is really a libtool program.
+	case $host in
+	*cygwin*|*mingw*)
+	    wrapper=`$echo $file | ${SED} -e 's,.exe$,,'`
+	    ;;
+	*)
+	    wrapper=$file
+	    ;;
+	esac
+	if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then
+	  notinst_deplibs=
+	  relink_command=
+
+	  # To insure that "foo" is sourced, and not "foo.exe",
+	  # finese the cygwin/MSYS system by explicitly sourcing "foo."
+	  # which disallows the automatic-append-.exe behavior.
+	  case $build in
+	  *cygwin* | *mingw*) wrapperdot=${wrapper}. ;;
+	  *) wrapperdot=${wrapper} ;;
+	  esac
+	  # If there is no directory component, then add one.
+	  case $file in
+	  */* | *\\*) . ${wrapperdot} ;;
+	  *) . ./${wrapperdot} ;;
+	  esac
+
+	  # Check the variables that should have been set.
+	  if test -z "$notinst_deplibs"; then
+	    $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2
+	    exit $EXIT_FAILURE
+	  fi
+
+	  finalize=yes
+	  for lib in $notinst_deplibs; do
+	    # Check to see that each library is installed.
+	    libdir=
+	    if test -f "$lib"; then
+	      # If there is no directory component, then add one.
+	      case $lib in
+	      */* | *\\*) . $lib ;;
+	      *) . ./$lib ;;
+	      esac
+	    fi
+	    libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+	    if test -n "$libdir" && test ! -f "$libfile"; then
+	      $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2
+	      finalize=no
+	    fi
+	  done
+
+	  relink_command=
+	  # To insure that "foo" is sourced, and not "foo.exe",
+	  # finese the cygwin/MSYS system by explicitly sourcing "foo."
+	  # which disallows the automatic-append-.exe behavior.
+	  case $build in
+	  *cygwin* | *mingw*) wrapperdot=${wrapper}. ;;
+	  *) wrapperdot=${wrapper} ;;
+	  esac
+	  # If there is no directory component, then add one.
+	  case $file in
+	  */* | *\\*) . ${wrapperdot} ;;
+	  *) . ./${wrapperdot} ;;
+	  esac
+
+	  outputname=
+	  if test "$fast_install" = no && test -n "$relink_command"; then
+	    if test "$finalize" = yes && test -z "$run"; then
+	      tmpdir="/tmp"
+	      test -n "$TMPDIR" && tmpdir="$TMPDIR"
+	      tmpdir="$tmpdir/libtool-$$"
+	      save_umask=`umask`
+	      umask 0077
+	      if $mkdir "$tmpdir"; then
+	        umask $save_umask
+	      else
+	        umask $save_umask
+		$echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2
+		continue
+	      fi
+	      file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'`
+	      outputname="$tmpdir/$file"
+	      # Replace the output file specification.
+	      relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+	      $show "$relink_command"
+	      if $run eval "$relink_command"; then :
+	      else
+		$echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+		${rm}r "$tmpdir"
+		continue
+	      fi
+	      file="$outputname"
+	    else
+	      $echo "$modename: warning: cannot relink \`$file'" 1>&2
+	    fi
+	  else
+	    # Install the binary that we compiled earlier.
+	    file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+	  fi
+	fi
+
+	# remove .exe since cygwin /usr/bin/install will append another
+	# one anyways
+	case $install_prog,$host in
+	*/usr/bin/install*,*cygwin*)
+	  case $file:$destfile in
+	  *.exe:*.exe)
+	    # this is ok
+	    ;;
+	  *.exe:*)
+	    destfile=$destfile.exe
+	    ;;
+	  *:*.exe)
+	    destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'`
+	    ;;
+	  esac
+	  ;;
+	esac
+	$show "$install_prog$stripme $file $destfile"
+	$run eval "$install_prog\$stripme \$file \$destfile" || exit $?
+	test -n "$outputname" && ${rm}r "$tmpdir"
+	;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+
+      $show "$install_prog $file $oldlib"
+      $run eval "$install_prog \$file \$oldlib" || exit $?
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+	$show "$old_striplib $oldlib"
+	$run eval "$old_striplib $oldlib" || exit $?
+      fi
+
+      # Do each command in the postinstall commands.
+      cmds=$old_postinstall_cmds
+      save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+	IFS="$save_ifs"
+	eval cmd=\"$cmd\"
+	$show "$cmd"
+	$run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$future_libdirs"; then
+      $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2
+    fi
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      test -n "$run" && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+    ;;
+
+  # libtool finish mode
+  finish)
+    modename="$modename: finish"
+    libdirs="$nonopt"
+    admincmds=
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for dir
+      do
+	libdirs="$libdirs $dir"
+      done
+
+      for libdir in $libdirs; do
+	if test -n "$finish_cmds"; then
+	  # Do each command in the finish commands.
+	  cmds=$finish_cmds
+	  save_ifs="$IFS"; IFS='~'
+	  for cmd in $cmds; do
+	    IFS="$save_ifs"
+	    eval cmd=\"$cmd\"
+	    $show "$cmd"
+	    $run eval "$cmd" || admincmds="$admincmds
+       $cmd"
+	  done
+	  IFS="$save_ifs"
+	fi
+	if test -n "$finish_eval"; then
+	  # Do the single finish_eval.
+	  eval cmds=\"$finish_eval\"
+	  $run eval "$cmds" || admincmds="$admincmds
+       $cmds"
+	fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    test "$show" = : && exit $EXIT_SUCCESS
+
+    $echo "----------------------------------------------------------------------"
+    $echo "Libraries have been installed in:"
+    for libdir in $libdirs; do
+      $echo "   $libdir"
+    done
+    $echo
+    $echo "If you ever happen to want to link against installed libraries"
+    $echo "in a given directory, LIBDIR, you must either use libtool, and"
+    $echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+    $echo "flag during linking and do at least one of the following:"
+    if test -n "$shlibpath_var"; then
+      $echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+      $echo "     during execution"
+    fi
+    if test -n "$runpath_var"; then
+      $echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+      $echo "     during linking"
+    fi
+    if test -n "$hardcode_libdir_flag_spec"; then
+      libdir=LIBDIR
+      eval flag=\"$hardcode_libdir_flag_spec\"
+
+      $echo "   - use the \`$flag' linker flag"
+    fi
+    if test -n "$admincmds"; then
+      $echo "   - have your system administrator run these commands:$admincmds"
+    fi
+    if test -f /etc/ld.so.conf; then
+      $echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+    fi
+    $echo
+    $echo "See any operating system documentation about shared libraries for"
+    $echo "more information, such as the ld(1) and ld.so(8) manual pages."
+    $echo "----------------------------------------------------------------------"
+    exit $EXIT_SUCCESS
+    ;;
+
+  # libtool execute mode
+  execute)
+    modename="$modename: execute"
+
+    # The first argument is the command name.
+    cmd="$nonopt"
+    if test -z "$cmd"; then
+      $echo "$modename: you must specify a COMMAND" 1>&2
+      $echo "$help"
+      exit $EXIT_FAILURE
+    fi
+
+    # Handle -dlopen flags immediately.
+    for file in $execute_dlfiles; do
+      if test ! -f "$file"; then
+	$echo "$modename: \`$file' is not a file" 1>&2
+	$echo "$help" 1>&2
+	exit $EXIT_FAILURE
+      fi
+
+      dir=
+      case $file in
+      *.la)
+	# Check to see that this really is a libtool archive.
+	if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+	else
+	  $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+	  $echo "$help" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+
+	# Read the libtool library.
+	dlname=
+	library_names=
+
+	# If there is no directory component, then add one.
+	case $file in
+	*/* | *\\*) . $file ;;
+	*) . ./$file ;;
+	esac
+
+	# Skip this library if it cannot be dlopened.
+	if test -z "$dlname"; then
+	  # Warn if it was a shared library.
+	  test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'"
+	  continue
+	fi
+
+	dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+	test "X$dir" = "X$file" && dir=.
+
+	if test -f "$dir/$objdir/$dlname"; then
+	  dir="$dir/$objdir"
+	else
+	  $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
+	  exit $EXIT_FAILURE
+	fi
+	;;
+
+      *.lo)
+	# Just add the directory containing the .lo file.
+	dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+	test "X$dir" = "X$file" && dir=.
+	;;
+
+      *)
+	$echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2
+	continue
+	;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+	eval "$shlibpath_var=\"\$dir\""
+      else
+	eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -*) ;;
+      *)
+	# Do a test to see if this is really a libtool program.
+	if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+	  # If there is no directory component, then add one.
+	  case $file in
+	  */* | *\\*) . $file ;;
+	  *) . ./$file ;;
+	  esac
+
+	  # Transform arg to wrapped name.
+	  file="$progdir/$program"
+	fi
+	;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"`
+      args="$args \"$file\""
+    done
+
+    if test -z "$run"; then
+      if test -n "$shlibpath_var"; then
+	# Export the shlibpath_var.
+	eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      if test "${save_LC_ALL+set}" = set; then
+	LC_ALL="$save_LC_ALL"; export LC_ALL
+      fi
+      if test "${save_LANG+set}" = set; then
+	LANG="$save_LANG"; export LANG
+      fi
+
+      # Now prepare to actually exec the command.
+      exec_cmd="\$cmd$args"
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+	eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\""
+	$echo "export $shlibpath_var"
+      fi
+      $echo "$cmd$args"
+      exit $EXIT_SUCCESS
+    fi
+    ;;
+
+  # libtool clean and uninstall mode
+  clean | uninstall)
+    modename="$modename: $mode"
+    rm="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) rm="$rm $arg"; rmforce=yes ;;
+      -*) rm="$rm $arg" ;;
+      *) files="$files $arg" ;;
+      esac
+    done
+
+    if test -z "$rm"; then
+      $echo "$modename: you must specify an RM program" 1>&2
+      $echo "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    rmdirs=
+
+    origobjdir="$objdir"
+    for file in $files; do
+      dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+      if test "X$dir" = "X$file"; then
+	dir=.
+	objdir="$origobjdir"
+      else
+	objdir="$dir/$origobjdir"
+      fi
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+      test "$mode" = uninstall && objdir="$dir"
+
+      # Remember objdir for removal later, being careful to avoid duplicates
+      if test "$mode" = clean; then
+	case " $rmdirs " in
+	  *" $objdir "*) ;;
+	  *) rmdirs="$rmdirs $objdir" ;;
+	esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if (test -L "$file") >/dev/null 2>&1 \
+	|| (test -h "$file") >/dev/null 2>&1 \
+	|| test -f "$file"; then
+	:
+      elif test -d "$file"; then
+	exit_status=1
+	continue
+      elif test "$rmforce" = yes; then
+	continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+	# Possibly a libtool archive, so verify it.
+	if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+	  . $dir/$name
+
+	  # Delete the libtool libraries and symlinks.
+	  for n in $library_names; do
+	    rmfiles="$rmfiles $objdir/$n"
+	  done
+	  test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+	  test "$mode" = clean && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+
+	  if test "$mode" = uninstall; then
+	    if test -n "$library_names"; then
+	      # Do each command in the postuninstall commands.
+	      cmds=$postuninstall_cmds
+	      save_ifs="$IFS"; IFS='~'
+	      for cmd in $cmds; do
+		IFS="$save_ifs"
+		eval cmd=\"$cmd\"
+		$show "$cmd"
+		$run eval "$cmd"
+		if test "$?" -ne 0 && test "$rmforce" != yes; then
+		  exit_status=1
+		fi
+	      done
+	      IFS="$save_ifs"
+	    fi
+
+	    if test -n "$old_library"; then
+	      # Do each command in the old_postuninstall commands.
+	      cmds=$old_postuninstall_cmds
+	      save_ifs="$IFS"; IFS='~'
+	      for cmd in $cmds; do
+		IFS="$save_ifs"
+		eval cmd=\"$cmd\"
+		$show "$cmd"
+		$run eval "$cmd"
+		if test "$?" -ne 0 && test "$rmforce" != yes; then
+		  exit_status=1
+		fi
+	      done
+	      IFS="$save_ifs"
+	    fi
+	    # FIXME: should reinstall the best remaining shared library.
+	  fi
+	fi
+	;;
+
+      *.lo)
+	# Possibly a libtool object, so verify it.
+	if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+
+	  # Read the .lo file
+	  . $dir/$name
+
+	  # Add PIC object to the list of files to remove.
+	  if test -n "$pic_object" \
+	     && test "$pic_object" != none; then
+	    rmfiles="$rmfiles $dir/$pic_object"
+	  fi
+
+	  # Add non-PIC object to the list of files to remove.
+	  if test -n "$non_pic_object" \
+	     && test "$non_pic_object" != none; then
+	    rmfiles="$rmfiles $dir/$non_pic_object"
+	  fi
+	fi
+	;;
+
+      *)
+	if test "$mode" = clean ; then
+	  noexename=$name
+	  case $file in
+	  *.exe)
+	    file=`$echo $file|${SED} 's,.exe$,,'`
+	    noexename=`$echo $name|${SED} 's,.exe$,,'`
+	    # $file with .exe has already been added to rmfiles,
+	    # add $file without .exe
+	    rmfiles="$rmfiles $file"
+	    ;;
+	  esac
+	  # Do a test to see if this is a libtool program.
+	  if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+	    relink_command=
+	    . $dir/$noexename
+
+	    # note $name still contains .exe if it was in $file originally
+	    # as does the version of $file that was added into $rmfiles
+	    rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+	    if test "$fast_install" = yes && test -n "$relink_command"; then
+	      rmfiles="$rmfiles $objdir/lt-$name"
+	    fi
+	    if test "X$noexename" != "X$name" ; then
+	      rmfiles="$rmfiles $objdir/lt-${noexename}.c"
+	    fi
+	  fi
+	fi
+	;;
+      esac
+      $show "$rm $rmfiles"
+      $run $rm $rmfiles || exit_status=1
+    done
+    objdir="$origobjdir"
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+	$show "rmdir $dir"
+	$run rmdir $dir >/dev/null 2>&1
+      fi
+    done
+
+    exit $exit_status
+    ;;
+
+  "")
+    $echo "$modename: you must specify a MODE" 1>&2
+    $echo "$generic_help" 1>&2
+    exit $EXIT_FAILURE
+    ;;
+  esac
+
+  if test -z "$exec_cmd"; then
+    $echo "$modename: invalid operation mode \`$mode'" 1>&2
+    $echo "$generic_help" 1>&2
+    exit $EXIT_FAILURE
+  fi
+fi # test -z "$show_help"
+
+if test -n "$exec_cmd"; then
+  eval exec $exec_cmd
+  exit $EXIT_FAILURE
+fi
+
+# We need to display help for each of the modes.
+case $mode in
+"") $echo \
+"Usage: $modename [OPTION]... [MODE-ARG]...
+
+Provide generalized library-building support services.
+
+    --config          show all configuration variables
+    --debug           enable verbose shell tracing
+-n, --dry-run         display commands without modifying any files
+    --features        display basic configuration information and exit
+    --finish          same as \`--mode=finish'
+    --help            display this help message and exit
+    --mode=MODE       use operation mode MODE [default=inferred from MODE-ARGS]
+    --quiet           same as \`--silent'
+    --silent          don't print informational messages
+    --tag=TAG         use configuration variables from tag TAG
+    --version         print version information
+
+MODE must be one of the following:
+
+      clean           remove files from the build directory
+      compile         compile a source file into a libtool object
+      execute         automatically set library path, then run a program
+      finish          complete the installation of libtool libraries
+      install         install libraries or executables
+      link            create a library or an executable
+      uninstall       remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE.  Try \`$modename --help --mode=MODE' for
+a more detailed description of MODE.
+
+Report bugs to <bug-libtool@gnu.org>."
+  exit $EXIT_SUCCESS
+  ;;
+
+clean)
+  $echo \
+"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+  ;;
+
+compile)
+  $echo \
+"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -prefer-pic       try to building PIC objects only
+  -prefer-non-pic   try to building non-PIC objects only
+  -static           always build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+  ;;
+
+execute)
+  $echo \
+"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+  ;;
+
+finish)
+  $echo \
+"Usage: $modename [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+  ;;
+
+install)
+  $echo \
+"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+  ;;
+
+link)
+  $echo \
+"Usage: $modename [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+		    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+		    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  Use a list of object files found in FILE to specify objects
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -static           do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+		    specify library version info [each variable defaults to 0]
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+  ;;
+
+uninstall)
+  $echo \
+"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+  ;;
+
+*)
+  $echo "$modename: invalid operation mode \`$mode'" 1>&2
+  $echo "$help" 1>&2
+  exit $EXIT_FAILURE
+  ;;
+esac
+
+$echo
+$echo "Try \`$modename --help' for more information about other modes."
+
+exit $EXIT_SUCCESS
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) $echo no;; *) $echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/userspace/libebtc/missing b/userspace/libebtc/missing
new file mode 100755
index 0000000..64b5f90
--- /dev/null
+++ b/userspace/libebtc/missing
@@ -0,0 +1,353 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2004-09-07.08
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004
+#   Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case "$1" in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  # Exit code 63 means version mismatch.  This often happens
+  # when the user try to use an ancient version of a tool on
+  # a file that requires a minimum version.  In this case we
+  # we should proceed has if the program had been absent, or
+  # if --run hadn't been passed.
+  if test $? = 63; then
+    run=:
+    msg="probably too old"
+  fi
+  ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  tar          try tar, gnutar, gtar, then tar without non-portable flags
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit 0
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit 0
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# Now exit if we have it, but it failed.  Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).
+case "$1" in
+  lex|yacc)
+    # Not GNU programs, they don't have --version.
+    ;;
+
+  tar)
+    if test -n "$run"; then
+       echo 1>&2 "ERROR: \`tar' requires --run"
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       exit 1
+    fi
+    ;;
+
+  *)
+    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+       # We have it, but it failed.
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       # Could not run --version or --help.  This is probably someone
+       # running `$TOOL --version' or `$TOOL --help' to check whether
+       # $TOOL exists and not knowing $TOOL uses missing.
+       exit 1
+    fi
+    ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+  aclocal*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case "$f" in
+      *:*) touch_files="$touch_files "`echo "$f" |
+				       sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+	   sed 's/\.am$/.in/' |
+	   while read f; do touch "$f"; done
+    ;;
+
+  autom4te)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.
+         You can get \`$1' as part of \`Autoconf' from any GNU
+         archive site."
+
+    file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
+    test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
+    if test -f "$file"; then
+	touch $file
+    else
+	test -z "$file" || exec >$file
+	echo "#! /bin/sh"
+	echo "# Created by GNU Automake missing as a replacement of"
+	echo "#  $ $@"
+	echo "exit 0"
+	chmod +x $file
+	exit 1
+    fi
+    ;;
+
+  bison|yacc)
+    echo 1>&2 "\
+WARNING: \`$1' $msg.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+	case "$LASTARG" in
+	*.y)
+	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+	    if [ -f "$SRCFILE" ]; then
+	         cp "$SRCFILE" y.tab.c
+	    fi
+	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+	    if [ -f "$SRCFILE" ]; then
+	         cp "$SRCFILE" y.tab.h
+	    fi
+	  ;;
+	esac
+    fi
+    if [ ! -f y.tab.h ]; then
+	echo >y.tab.h
+    fi
+    if [ ! -f y.tab.c ]; then
+	echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex|flex)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+	case "$LASTARG" in
+	*.l)
+	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+	    if [ -f "$SRCFILE" ]; then
+	         cp "$SRCFILE" lex.yy.c
+	    fi
+	  ;;
+	esac
+    fi
+    if [ ! -f lex.yy.c ]; then
+	echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+	 you modified a dependency of a manual page.  You may need the
+	 \`Help2man' package in order for those modifications to take
+	 effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+	file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+    fi
+    if [ -f "$file" ]; then
+	touch $file
+    else
+	test -z "$file" || exec >$file
+	echo ".ab help2man is required to generate this page"
+	exit 1
+    fi
+    ;;
+
+  makeinfo)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+    fi
+    touch $file
+    ;;
+
+  tar)
+    shift
+
+    # We have already tried tar in the generic part.
+    # Look for gnutar/gtar before invocation to avoid ugly error
+    # messages.
+    if (gnutar --version > /dev/null 2>&1); then
+       gnutar "$@" && exit 0
+    fi
+    if (gtar --version > /dev/null 2>&1); then
+       gtar "$@" && exit 0
+    fi
+    firstarg="$1"
+    if shift; then
+	case "$firstarg" in
+	*o*)
+	    firstarg=`echo "$firstarg" | sed s/o//`
+	    tar "$firstarg" "$@" && exit 0
+	    ;;
+	esac
+	case "$firstarg" in
+	*h*)
+	    firstarg=`echo "$firstarg" | sed s/h//`
+	    tar "$firstarg" "$@" && exit 0
+	    ;;
+	esac
+    fi
+
+    echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+         You may want to install GNU tar or Free paxutils, or check the
+         command line arguments."
+    exit 1
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequisites for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/userspace/libebtc/src/Makefile.am b/userspace/libebtc/src/Makefile.am
new file mode 100644
index 0000000..972a012
--- /dev/null
+++ b/userspace/libebtc/src/Makefile.am
@@ -0,0 +1,55 @@
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+
+LT_CUR                      = 1
+LT_REV                      = 0
+LT_AGE                      = 1
+
+MAINTAINERCLEANFILES        = Makefile.in
+
+AM_CFLAGS                   = @CFLAGS@ -I../include
+AM_LDFLAGS                  = @LDFLAGS@
+
+EXTRA_DIST                  =
+
+lib_LTLIBRARIES             = libebtc.la
+
+libebtc_la_SOURCES          = ebtc.c
+libebtc_la_LDFLAGS          = -version-info ${LT_CUR}:${LT_REV}:${LT_AGE}
+
+
diff --git a/userspace/libebtc/src/Makefile.in b/userspace/libebtc/src/Makefile.in
new file mode 100644
index 0000000..3327e83
--- /dev/null
+++ b/userspace/libebtc/src/Makefile.in
@@ -0,0 +1,502 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+SOURCES = $(libebtc_la_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libebtc_la_LIBADD =
+am_libebtc_la_OBJECTS = ebtc.lo
+libebtc_la_OBJECTS = $(am_libebtc_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libebtc_la_SOURCES)
+DIST_SOURCES = $(libebtc_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+EBTC_LT_AGE = @EBTC_LT_AGE@
+EBTC_LT_CURRENT = @EBTC_LT_CURRENT@
+EBTC_LT_REVISION = @EBTC_LT_REVISION@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+LT_CUR = 1
+LT_REV = 0
+LT_AGE = 1
+MAINTAINERCLEANFILES = Makefile.in
+AM_CFLAGS = @CFLAGS@ -I../include
+AM_LDFLAGS = @LDFLAGS@
+EXTRA_DIST = 
+lib_LTLIBRARIES = libebtc.la
+libebtc_la_SOURCES = ebtc.c
+libebtc_la_LDFLAGS = -version-info ${LT_CUR}:${LT_REV}:${LT_AGE}
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    f=$(am__strip_dir) \
+	    echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+	    $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+	  else :; fi; \
+	done
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@set -x; list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  p=$(am__strip_dir) \
+	  echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+	  $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+	  test "$$dir" != "$$p" || dir=.; \
+	  echo "rm -f \"$${dir}/so_locations\""; \
+	  rm -f "$${dir}/so_locations"; \
+	done
+libebtc.la: $(libebtc_la_OBJECTS) $(libebtc_la_DEPENDENCIES) 
+	$(LINK) -rpath $(libdir) $(libebtc_la_LDFLAGS) $(libebtc_la_OBJECTS) $(libebtc_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ebtc.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    dir="/$$dir"; \
+	    $(mkdir_p) "$(distdir)$$dir"; \
+	  else \
+	    dir=''; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)"; do \
+	  test -z "$$dir" || $(mkdir_p) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-exec \
+	install-exec-am install-info install-info-am \
+	install-libLTLIBRARIES install-man install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags uninstall uninstall-am uninstall-info-am \
+	uninstall-libLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/userspace/libebtc/src/ebtc.c b/userspace/libebtc/src/ebtc.c
new file mode 100644
index 0000000..d7b68ba
--- /dev/null
+++ b/userspace/libebtc/src/ebtc.c
@@ -0,0 +1,2157 @@
+/*
+ * ==[ FILENAME: ebtc.c ]=======================================================
+ *
+ *  Project
+ *
+ *      Library for ethernet bridge tables.
+ *
+ *
+ *  Description
+ *
+ *      See project
+ *
+ *
+ *  Copyright
+ *
+ *      Copyright 2005 by Jens Götze
+ *      All rights reserved.
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+ *      USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <libebtc.h>
+
+
+/* Macros */
+
+#define SUCCESS                         0
+#define ERR_CHAINNOTFOUND               1
+#define ERR_CHAINEXIST                  2
+#define ERR_POLICYNOTFOUND              3
+#define ERR_ALLOCATEMEM                 4
+#define ERR_RULENUM                     5
+#define ERR_BUILTINCHAIN                6
+#define ERR_RAWSOCKET                   7
+#define ERR_GETINFO                     8
+#define ERR_GETENTRIES                  9
+#define ERR_SETENTRIES                  10
+#define ERR_SETCOUNTERS                 11
+#define ERR_BADENTRY                    12
+#define ERR_STDCHAINNOTALLOW            13
+#define ERR_ENTRYTARGETINVALID          14
+
+
+/* Types */
+
+typedef struct rule_list_st rule_list_t;
+
+typedef struct chain_list_st chain_list_t;
+
+typedef struct chain2id_st chain2id_t;
+
+
+/* Structures */
+
+struct rule_list_st {
+
+    rule_list_t *next;
+
+    /* Payload */
+
+    struct ebt_entry *entry;
+
+    struct {
+
+        int changed;
+
+        int offset;
+
+        struct ebt_counter counter;
+
+    } counter;
+
+};
+
+struct chain_list_st {
+
+    chain_list_t *next;
+
+    int id; /* used for chain jump */
+
+    int offset;
+
+    /* Payload */
+
+    int hookid;
+
+    struct ebt_entries entries;
+
+    struct {
+
+        int count;
+
+        unsigned int size;
+
+        rule_list_t *first;
+
+        rule_list_t *cur;
+
+        rule_list_t *last;
+
+    } rules;
+
+};
+
+struct chain2id_st {
+
+    const char name[EBT_CHAIN_MAXNAMELEN];
+
+    int id;
+
+};
+
+struct ebtc_handle_st {
+
+    /* Communication backend to kernel */
+
+    int fd;
+
+    struct ebt_replace replace;
+
+    /* Errorhandling */
+
+    unsigned int error_no;
+
+    /* Cache */
+
+    struct {
+
+        unsigned int num_counters;
+
+    } cache;
+
+    /* Payload */
+
+    int changed;
+
+    struct {
+
+        int count;
+
+        int last_id;
+
+        chain_list_t *first;
+
+        chain_list_t *cur;
+
+        chain_list_t *last;
+
+    } chains;
+
+};
+
+
+/* Global variables */
+
+static chain2id_t targets[] = {
+    { "ACCEPT",         EBT_ACCEPT          },
+    { "DROP",           EBT_DROP            },
+    { "CONTINUE",       EBT_CONTINUE        },
+    { "RETURN",         EBT_RETURN          },
+    { "",               0                   }
+};
+
+static chain2id_t builtinchains[] = {
+    { "PREROUTING",     NF_BR_PRE_ROUTING,  },
+    { "INPUT",          NF_BR_LOCAL_IN      },
+    { "FORWARD",        NF_BR_FORWARD       },
+    { "OUTPUT",         NF_BR_LOCAL_OUT     },
+    { "POSTROUTING",    NF_BR_POST_ROUTING, },
+    { "BROUTING",       NF_BR_BROUTING,     },
+    { "",               0                   }
+};
+
+static struct {
+
+    unsigned int id;
+
+    char *msg;
+
+} error_msg[] = {
+    { SUCCESS,                  "success" },
+    { ERR_CHAINNOTFOUND,        "chain not found" },
+    { ERR_CHAINEXIST,           "chain already exist" },
+    { ERR_POLICYNOTFOUND,       "policy not exist" },
+    { ERR_ALLOCATEMEM,          "can't allocate memory" },
+    { ERR_RULENUM,              "rule number is out of range" },
+    { ERR_BUILTINCHAIN,         "delete denied this chain is a builtin chain" },
+    { ERR_RAWSOCKET,            "can't open raw socket" },
+    { ERR_GETINFO,              "can't get informations" },
+    { ERR_GETENTRIES,           "can't get entries" },
+    { ERR_SETENTRIES,           "can't set entries" },
+    { ERR_SETCOUNTERS,          "can't set counters" },
+    { ERR_BADENTRY,             "entry is not valid" },
+    { ERR_STDCHAINNOTALLOW,     "standard chain is not allowed" },
+    { ERR_ENTRYTARGETINVALID,   "target of entry is invalid" }
+};
+
+static struct {
+
+    /* Errorhandling */
+
+    unsigned int error_no;
+
+} self_private;
+
+
+static chain_list_t *find_chain (const char *chainname,
+                                 const ebtc_handle_t handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain = handle->chains.first;
+
+
+
+/* ---- CODE ---- */
+
+    for (; chain; chain = chain->next) {
+
+        if (!strcmp(chain->entries.name, chainname))
+            return chain;
+
+    }
+
+    return NULL;
+
+}
+
+
+static int check_entry (const ebt_entry_t *entry)
+{
+
+/* ---- VAR ---- */
+
+    ebt_standard_target_t   *std_target;
+
+    ebt_entry_target_t      *target;
+
+
+
+/* ---- CODE ---- */
+
+    if (!(entry->bitmask & EBT_ENTRY_OR_ENTRIES))
+        return -1;
+
+    /* Check offsets */
+
+    if (!entry->watchers_offset)
+        return -1;
+
+    if (!entry->target_offset)
+        return -1;
+
+    if (!entry->next_offset)
+        return -1;
+
+    if (entry->watchers_offset > entry->target_offset)
+        return -1;
+
+    if (entry->target_offset > entry->next_offset)
+        return -1;
+
+    return 0;
+
+}
+
+
+int ebtc_is_chain (const char *chainname, const ebtc_handle_t handle)
+{
+
+/* ---- CODE ---- */
+
+    return find_chain(chainname, handle) ? 0 : -1;
+
+}
+
+
+const char *ebtc_first_chain (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    (*handle)->chains.cur = chain = (*handle)->chains.first;
+
+    return chain->entries.name;
+
+}
+
+
+const char *ebtc_next_chain (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    chain = (*handle)->chains.cur;
+
+    if (!chain)
+        return NULL;
+
+    (*handle)->chains.cur = chain = chain->next;
+
+    return chain ? chain->entries.name : NULL;
+
+}
+
+
+const struct ebt_entry *ebtc_first_rule (const char *chainname,
+                                         ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return NULL;
+
+    }
+
+    rule = chain->rules.cur = chain->rules.first;
+
+    return rule ? rule->entry : NULL;
+
+}
+
+
+const struct ebt_entry *ebtc_next_rule (const char *chainname,
+                                        ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return NULL;
+
+    }
+
+    if (!chain->rules.cur)
+        return NULL;
+
+    rule = chain->rules.cur = chain->rules.cur->next;
+
+    (*handle)->error_no = SUCCESS;
+
+    return rule ? rule->entry : NULL;
+
+}
+
+
+const char *ebtc_get_target (const struct ebt_entry *entry,
+                             const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    char                    *ptr;
+
+    ebt_entry_target_t      *target;
+
+    ebt_standard_target_t   *std_target;
+
+    chain2id_t              *chain2id;
+
+
+
+/* ---- CODE ---- */
+
+    ptr = (char *)entry;
+    ptr += entry->target_offset;
+
+    target = (struct ebt_entry_target *)ptr;
+
+    if (strcmp(target->u.name, "standard"))
+        return target->u.name;
+
+    /* Convert id to ascii */
+
+    std_target = (ebt_standard_target_t *)target;
+
+    for (chain2id = targets; *chain2id->name; chain2id++) {
+
+        if (std_target->verdict == chain2id->id)
+            return chain2id->name;
+
+    }
+
+    return NULL;
+
+}
+
+
+int ebtc_is_builtin (const char *chainname, const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain)
+        return -1;
+
+    return chain->hookid == NF_BR_NUMHOOKS ? -1 : 0;
+
+}
+
+
+int ebtc_set_policy (const char *chainname, const char *policy,
+                     ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    int                     id = 0;
+
+    chain_list_t            *chain = (*handle)->chains.first;
+
+
+
+/* ---- CODE ---- */
+
+    /* Transform policy to id */
+
+    for (i = 0; ; i++) {
+
+        if (!*targets[i].name) {
+
+            (*handle)->error_no = ERR_POLICYNOTFOUND;
+
+            return -1;
+
+        }
+
+        if (!strcmp(policy, targets[i].name)) {
+
+            id = targets[i].id;
+            break;
+
+        }
+
+    }
+
+    /* Search chain and set policy */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    if (chain->entries.policy != id) {
+
+        chain->entries.policy = id;
+        (*handle)->changed = EBTC_TRUE;
+
+    }
+
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+const char *ebtc_get_policy (const char *chainname, const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    chain_list_t            *chain;
+
+    chain2id_t              *chain2id;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return NULL;
+
+    }
+
+    /* Convert id to ascii */
+
+    for (chain2id = targets; *chain2id->name; chain2id++) {
+
+        if (chain->entries.policy == chain2id->id) {
+
+            (*handle)->error_no = SUCCESS;
+
+            return chain2id->name;
+
+        }
+
+    }
+
+    return NULL;
+
+}
+
+
+int ebtc_insert_entry (const char *chainname, const struct ebt_entry *entry,
+                       unsigned int rulenum, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule_last = NULL;
+
+    rule_list_t             *rule_cur;
+
+    rule_list_t             *rule;
+
+    unsigned int            size;
+
+
+
+/* ---- CODE ---- */
+
+    if (check_entry(entry)) {
+
+        (*handle)->error_no = ERR_BADENTRY;
+
+        return -1;
+
+    }
+
+    /* Search for chain in chain list */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Copy entry and append to chain */
+
+    size = sizeof(rule_list_t) + entry->next_offset;
+    rule = (rule_list_t *)malloc(size);
+
+    if (!rule) {
+
+        (*handle)->error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    memset(rule, 0, size);
+
+    rule->entry = (ebt_entry_t *)(rule + 1);
+    memcpy(rule->entry, entry, entry->next_offset);
+
+    rule_cur = chain->rules.first;
+
+    if (rule_cur) {
+
+        for (; rulenum > 0 && rule_cur; rulenum--) {
+
+            rule_last = rule_cur;
+            rule_cur = rule_cur->next;
+
+        }
+
+        if (rulenum || !rule_cur) {
+
+            (*handle)->error_no = ERR_RULENUM;
+
+            return -1;
+
+        }
+
+        if (rule_last)
+            rule_last->next = rule;
+        else
+            chain->rules.first = rule;
+
+        rule->next = rule_cur;
+
+    } else {
+
+        if (rulenum != 0) {
+
+            (*handle)->error_no = ERR_RULENUM;
+
+            return -1;
+
+        }
+
+        chain->rules.first = chain->rules.last = rule;
+
+    }
+
+    chain->rules.count++;
+    chain->rules.size += entry->next_offset;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_replace_entry (const char *chainname, const struct ebt_entry *entry,
+                        unsigned int rulenum, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule_last = NULL;
+
+    rule_list_t             *rule_old;
+
+    rule_list_t             *rule_new;
+
+    unsigned int            size;
+
+
+
+/* ---- CODE ---- */
+
+    if (check_entry(entry)) {
+
+        (*handle)->error_no = ERR_BADENTRY;
+
+        return -1;
+
+    }
+
+    /* Search for chain in chain list */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Copy entry and replace in chain */
+
+    size = sizeof(rule_list_t) + entry->next_offset;
+    rule_new = (rule_list_t *)malloc(size);
+
+    if (!rule_new) {
+
+        (*handle)->error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    memset(rule_new, 0, size);
+
+    rule_new->entry = (ebt_entry_t *)(rule_new + 1);
+    memcpy(rule_new->entry, entry, entry->next_offset);
+
+    rule_old = chain->rules.first;
+
+    if (!rule_old) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    for (; rulenum > 0 && rule_old; rulenum--) {
+
+        rule_last = rule_old;
+        rule_old = rule_old->next;
+
+    }
+
+    if (rulenum || !rule_old) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    if (rule_last)
+        rule_last->next = rule_new;
+    else
+        chain->rules.first = rule_new;
+
+    rule_new->next = rule_old->next;
+
+    if (!rule_new->next)
+        chain->rules.last = rule_new;
+
+    chain->rules.size -= rule_old->entry->next_offset;
+    chain->rules.size += entry->next_offset;
+
+    (*handle)->changed = EBTC_TRUE;
+
+    /* Clean up */
+
+    free(rule_old);
+
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_append_entry (const char *chainname, const struct ebt_entry *entry,
+                       ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain = (*handle)->chains.first;
+
+    rule_list_t             *rule;
+
+    unsigned int            size;
+
+
+
+/* ---- CODE ---- */
+
+    if (check_entry(entry)) {
+
+        (*handle)->error_no = ERR_BADENTRY;
+
+        return -1;
+
+    }
+
+    /* Search for chain in chain list */
+
+    while (chain) {
+
+        if (!strcmp(chain->entries.name, chainname))
+            break;
+
+        chain = chain->next;
+
+    }
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Copy entry and insert into chain */
+
+    size = sizeof(rule_list_t) + entry->next_offset;
+    rule = (rule_list_t *)malloc(size);
+
+    if (!rule) {
+
+        (*handle)->error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    memset(rule, 0, size);
+
+    rule->entry = (ebt_entry_t *)(rule + 1);
+    memcpy(rule->entry, entry, entry->next_offset);
+
+    if (chain->rules.last) {
+
+        chain->rules.last->next = rule;
+        chain->rules.last = rule;
+
+    } else
+        chain->rules.first = chain->rules.last = rule;
+
+    chain->rules.count++;
+    chain->rules.size += entry->next_offset;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_delete_entry (const char *chainname, unsigned int rulenum,
+                       ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule_last = NULL;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Find and delete rule */
+
+    rule = chain->rules.first;
+
+    if (!rule) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    for (; rulenum > 0 && rule; rulenum--) {
+
+        rule_last = rule;
+        rule = rule->next;
+
+    }
+
+    if (rulenum || !rule) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    if (rule_last)
+        rule_last->next = rule->next;
+    else
+        chain->rules.first = rule->next;
+
+    if (!rule->next)
+        chain->rules.last = rule_last;
+
+    chain->rules.count--;
+    chain->rules.size -= rule->entry->next_offset;
+
+    (*handle)->changed = EBTC_TRUE;
+
+    /* Clean up */
+
+    free(rule);
+
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_flush_entries (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+    rule_list_t             *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Free all rules */
+
+    rule = chain->rules.first;
+
+    while (rule) {
+
+        rule_next = rule->next;
+
+        free(rule);
+
+        rule = rule_next;
+
+    }
+
+    memset(&chain->rules, 0, sizeof(chain->rules));
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_zero_entries (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Iterate set zero in rule list */
+
+    rule = chain->rules.first;
+
+    while (rule) {
+
+        memset(&rule->counter.counter, 0, sizeof(ebt_counter_t));
+        rule->counter.changed = EBTC_TRUE;
+
+        rule = rule->next;
+
+    }
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_rename_chain (const char *chainname_old, const char *chainname_new,
+                       ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname_old, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    if (chain->hookid != NF_BR_NUMHOOKS) { 
+
+        (*handle)->error_no = ERR_BUILTINCHAIN;
+
+        return -1;
+
+    }
+
+    /* Set new chainname */
+
+    snprintf(chain->entries.name, EBT_CHAIN_MAXNAMELEN, "%s", chainname_new);
+
+    (*handle)->changed = EBTC_TRUE;
+
+    return 0;
+
+}
+
+
+int ebtc_create_chain (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    /* Exist chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (chain) {
+
+        (*handle)->error_no = ERR_CHAINEXIST;
+
+        return -1;
+
+    }
+
+    /* Allocate new chain */
+
+    chain = (chain_list_t *)malloc(sizeof(chain_list_t));
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    memset(chain, 0, sizeof(chain_list_t));
+
+    chain->hookid = NF_BR_NUMHOOKS;
+    chain->id = (*handle)->chains.last_id++;
+
+    snprintf(chain->entries.name, EBT_CHAIN_MAXNAMELEN, "%s", chainname);
+    chain->entries.policy = EBT_ACCEPT;
+
+    if ((*handle)->chains.last) {
+
+        (*handle)->chains.last->next = chain;
+        (*handle)->chains.last = chain;
+
+    } else
+        (*handle)->chains.last = (*handle)->chains.first = chain;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_delete_chain (const char *chainname, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain = (*handle)->chains.first;
+
+    chain_list_t            *chain_last = NULL;
+
+    rule_list_t             *rule;
+
+    rule_list_t             *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    for (; chain; chain_last = chain, chain = chain->next) {
+
+        if (!strcmp(chain->entries.name, chainname))
+            break;
+
+    }
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    if (chain->hookid != NF_BR_NUMHOOKS) {
+
+        (*handle)->error_no = ERR_BUILTINCHAIN;
+
+        return -1;
+
+    }
+
+    /* Free all rules */
+
+    rule = chain->rules.first;
+
+    while (rule) {
+
+        rule_next = rule->next;
+
+        free(rule);
+
+        rule = rule_next;
+
+    }
+
+    /* Remove chain from list */
+
+    if (chain_last)
+        chain_last->next = chain->next;
+    else
+        (*handle)->chains.first = chain->next;
+
+    if (!chain->next)
+        (*handle)->chains.last = chain_last;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    /* Clean up */
+
+    free(chain);
+
+    return 0;
+
+}
+
+
+const struct ebt_counter *ebtc_read_counter (const char *chainname,
+                                             unsigned int rulenum,
+                                             ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return NULL;
+
+    }
+
+    /* Find rule */
+
+    rule = chain->rules.first;
+
+    for (; rulenum > 0 && rule; rulenum--)
+        rule = rule->next;
+
+    if (rulenum || !rule) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return NULL;
+
+    }
+
+    (*handle)->error_no = SUCCESS;
+
+    return &rule->counter.counter;
+
+}
+
+
+int ebtc_zero_counter (const char *chainname, unsigned int rulenum,
+                       ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Find rule */
+
+    rule = chain->rules.first;
+
+    for (; rulenum > 0 && rule; rulenum--)
+        rule = rule->next;
+
+    if (rulenum || !rule) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    memset(&rule->counter.counter, 0, sizeof(ebt_counter_t));
+    rule->counter.changed = EBTC_TRUE;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_set_counter (const char *chainname, unsigned int rulenum,
+                      const struct ebt_counter *counter, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+
+
+/* ---- CODE ---- */
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    /* Find rule */
+
+    rule = chain->rules.first;
+
+    for (; rulenum > 0 && rule; rulenum--)
+        rule = rule->next;
+
+    if (rulenum || !rule) {
+
+        (*handle)->error_no = ERR_RULENUM;
+
+        return -1;
+
+    }
+
+    memcpy(&rule->counter.counter, counter, sizeof(ebt_counter_t));
+    rule->counter.changed = EBTC_TRUE;
+
+    (*handle)->changed = EBTC_TRUE;
+    (*handle)->error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+int ebtc_target_jumptochain (ebt_standard_target_t *target, char *chainname,
+                             ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    chain_list_t            *chain;
+
+
+
+/* ---- CODE ---- */
+
+    /* Prepare basics of target */
+
+    target->target.target_size = EBTC_SIZEOF(ebt_standard_target_t);
+    target->target.target_size -= EBTC_SIZEOF(ebt_entry_target_t);
+
+    snprintf(target->target.u.name, EBT_FUNCTION_MAXNAMELEN, "standard");
+
+    /* Check for standard targets */
+
+    for (i = 0; *targets[i].name; i++) {
+
+        if (!strcmp(chainname, targets[i].name)) {
+
+            target->verdict = targets[i].id;
+
+            return 0;
+
+        }
+
+    }
+
+    /* Check for standard targets */
+
+    for (i = 0; *targets[i].name; i++) {
+
+        if (!strcmp(chainname, targets[i].name)) {
+
+            target->verdict = targets[i].id;
+
+            return 0;
+
+        }
+
+    }
+
+    /* Find chain */
+
+    chain = find_chain(chainname, *handle);
+
+    if (!chain) {
+
+        (*handle)->error_no = ERR_CHAINNOTFOUND;
+
+        return -1;
+
+    }
+
+    if (chain->hookid != NF_BR_NUMHOOKS) {
+
+        (*handle)->error_no = ERR_STDCHAINNOTALLOW;
+
+        return -1;
+
+    }
+
+    target->verdict = chain->id;
+
+    return 0;
+
+}
+
+
+ebtc_handle_t ebtc_init (const char *tablename, int options)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    int                     optlen;
+
+    int                     optname;
+
+    int                     counter;
+
+    unsigned int            size;
+
+    unsigned int            entry_count;
+
+    unsigned int            rule_size;
+
+    ebt_replace_t           *replace;
+
+    ebt_entries_t           *entries;
+
+    ebt_entry_t             *entry;
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+    ebtc_handle_t           handle;
+
+
+
+/* ---- CODE ---- */
+
+    /* Allocate memory for handle and initialize this object */
+
+    handle = (ebtc_handle_t)malloc(sizeof(struct ebtc_handle_st));
+
+    if (!handle) {
+
+        self_private.error_no = ERR_ALLOCATEMEM;
+
+        return NULL;
+
+    }
+
+    memset(handle, 0, sizeof(struct ebtc_handle_st));
+
+    handle->changed = options & EBTC_INIT_WITHFLUSH ? EBTC_TRUE : EBTC_FALSE;
+    replace = &handle->replace;
+
+    /* Open raw socket */
+
+    handle->fd = socket(AF_INET, SOCK_RAW, PF_INET);
+
+    if (handle->fd == -1) {
+
+        free(handle);
+
+        self_private.error_no = ERR_RAWSOCKET;
+
+        return NULL;
+
+    }
+
+    /* Get infomations */
+
+    memset(replace, 0, sizeof(ebt_replace_t));
+    snprintf(replace->name, EBT_TABLE_MAXNAMELEN, "%s", tablename);
+
+    optlen = sizeof(ebt_replace_t);
+    optname = options & EBTC_INIT_WITHFLUSH ? EBT_SO_GET_INIT_INFO :
+                                              EBT_SO_GET_INFO;
+
+    if (getsockopt(handle->fd, IPPROTO_IP, optname, replace, &optlen)) {
+
+        free(handle);
+
+        self_private.error_no = ERR_GETINFO;
+
+        return NULL;
+
+    }
+
+    /* Get entries */
+
+    if (replace->nentries) {
+
+        size = replace->nentries * sizeof(ebt_counter_t);
+        replace->counters = (ebt_counter_t *)malloc(size);
+
+        if (!replace->counters) {
+
+            close(handle->fd);
+            free(handle);
+
+            self_private.error_no = ERR_ALLOCATEMEM;
+
+            return NULL;
+
+        }
+
+    } else {
+
+        size = 0;
+        replace->counters = NULL;
+
+    }
+
+    handle->cache.num_counters = replace->num_counters = replace->nentries;
+    replace->entries = malloc(replace->entries_size);
+
+    if (!replace->entries) {
+
+        if (replace->counters)
+            free(replace->counters);
+
+        close(handle->fd);
+        free(handle);
+
+        self_private.error_no = ERR_ALLOCATEMEM;
+
+        return NULL;
+
+    }
+
+    optlen += replace->entries_size;
+    optlen += replace->num_counters * sizeof(ebt_counter_t);
+    optname = options & EBTC_INIT_WITHFLUSH ? EBT_SO_GET_INIT_ENTRIES :
+                                              EBT_SO_GET_ENTRIES;
+
+    if (getsockopt(handle->fd, IPPROTO_IP, optname, replace, &optlen)) {
+
+        close(handle->fd);
+
+        if (replace->counters)
+            free(replace->counters);
+
+        free(replace->entries);
+        free(handle);
+
+        self_private.error_no = ERR_GETENTRIES;
+
+        return NULL;
+
+    }
+
+    /* Split one block from kernel into lists */
+
+    size = replace->entries_size;
+    entries = (ebt_entries_t *)replace->entries;
+
+    for (; size > 0; size -= sizeof(ebt_entries_t)) {
+
+        /* Allocate and fill a container for chain */
+
+        chain = (chain_list_t *)malloc(sizeof(chain_list_t));
+
+        if (!chain) {
+
+            ebtc_free(&handle);
+
+            self_private.error_no = ERR_ALLOCATEMEM;
+
+            return NULL;
+
+        }
+
+        memset(chain, 0, sizeof(chain_list_t));
+        memcpy(&chain->entries, entries, sizeof(ebt_entries_t));
+
+        chain->hookid = NF_BR_NUMHOOKS;
+        chain->id = handle->chains.last_id++;
+
+        for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+
+            if (!strcmp(entries->name, builtinchains[i].name)) {
+
+                chain->hookid = builtinchains[i].id;
+                break;
+
+            }
+
+        }
+
+        if (handle->chains.last) {
+
+            handle->chains.last->next = chain;
+            handle->chains.last = chain;
+
+        } else
+            handle->chains.last = handle->chains.first = chain;
+
+        handle->chains.count++;
+
+        /* Put entries into rule list of current chain */
+
+        entry_count = entries->nentries;
+        counter = entries->counter_offset;
+
+        entries++;
+
+        if (entry_count) {
+
+            entry = (ebt_entry_t *)entries;
+            counter = chain->entries.counter_offset;
+
+            for (; entry_count > 0; entry_count--) {
+
+                /* Allocate memory for rule */
+
+                rule_size = sizeof(rule_list_t) + entry->next_offset;
+                rule = (rule_list_t *)malloc(rule_size);
+
+                if (!rule) {
+
+                    ebtc_free(&handle);
+
+                    self_private.error_no = ERR_ALLOCATEMEM;
+
+                    return NULL;
+
+                }
+
+                /* Initialize rule */
+
+                memset(rule, 0, sizeof(rule_list_t));
+
+                rule->counter.changed = EBTC_FALSE;
+                rule->counter.offset = counter;
+
+                rule->entry = (ebt_entry_t *)(rule + 1);
+                memcpy(rule->entry, entry, entry->next_offset);
+
+                chain->rules.size += entry->next_offset;
+
+                memcpy(&rule->counter.counter, &replace->counters[counter++],
+                       sizeof(ebt_counter_t));
+
+                if (chain->rules.last) {
+
+                    chain->rules.last->next = rule;
+                    chain->rules.last = rule;
+
+                } else
+                    chain->rules.last = chain->rules.first = rule;
+
+                chain->rules.count++;
+
+                /* Jump to next entry */
+
+                size -= entry->next_offset;
+                entry = (ebt_entry_t *)((char *)entry +
+                                             entry->next_offset);
+
+            }
+
+            entries = (ebt_entries_t *)entry;
+
+        }
+
+    }
+
+    /* Clean up */
+
+    if (replace->counters) {
+
+        free(replace->counters);
+        replace->counters = NULL;
+
+    }
+
+    free(replace->entries);
+    replace->entries = NULL;
+
+    handle->error_no = SUCCESS;
+
+    return handle;
+
+}
+
+
+static int precommit_standard_target (chain_list_t *chain, ebt_entry_t *entry)
+{
+
+/* ---- VAR ---- */
+
+    ebt_standard_target_t   *target;
+
+
+
+/* ---- CODE ---- */
+
+    /* Check for standard target */
+
+    target = (ebt_standard_target_t *)EBTC_ADDOFFSET(entry,
+                                                     entry->target_offset);
+
+    if (strncmp(target->target.u.name, "standard", EBT_FUNCTION_MAXNAMELEN))
+        return 0;
+
+    for (; chain; chain = chain->next) {
+
+        if (chain->id == target->verdict) {
+
+            target->verdict = chain->offset;
+
+            return 0;
+
+        }
+
+    }
+
+    return 0;
+
+}
+
+
+int ebtc_commit (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    socklen_t               optlen;
+
+    int                     result;
+
+    int                     fd;
+
+    int                     i;
+
+    unsigned int            size;
+
+    unsigned int            size_entry;
+
+    unsigned int            size_entries = 0;
+
+    unsigned int            size_counters = 0;
+
+    unsigned int            counter_offset = 0;
+
+    unsigned int            count_entries = 0;
+
+    chain_list_t            *chain;
+
+    rule_list_t             *rule;
+
+    ebt_replace_t           *replace;
+
+    ebt_entries_t           *entries;
+
+    ebt_entry_t             *entry;
+
+    ebt_counter_t           *counters;
+
+    ebt_counter_t           *counters_back;
+
+
+
+/* ---- CODE ---- */
+
+    /* Check of all conditions */
+
+    if ((*handle)->changed == EBTC_FALSE) {
+
+        ebtc_free(handle);
+
+        self_private.error_no = SUCCESS;
+
+        return 0;
+
+    }
+
+    /* Collect all size informations for entries */
+
+    for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+        chain->offset = size_entries;
+
+        size_entries += sizeof(ebt_entries_t);
+        size_entries += chain->rules.size;
+
+        size_counters += sizeof(ebt_counter_t) * chain->rules.count;
+        count_entries += chain->rules.count;
+
+    }
+
+    /* Allocate */
+
+    replace = (ebt_replace_t *)malloc(sizeof(ebt_replace_t));
+
+    if (!replace) {
+
+        ebtc_free(handle);
+
+        self_private.error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    entries = (ebt_entries_t *)malloc(size_entries);
+
+    if (!entries) {
+
+        free(replace);
+        ebtc_free(handle);
+
+        self_private.error_no = ERR_ALLOCATEMEM;
+
+        return -1;
+
+    }
+
+    /* Pack all entries in a big block */
+
+    memcpy(replace, &(*handle)->replace, sizeof(ebt_replace_t));
+
+    replace->entries_size = size_entries;
+    replace->nentries = count_entries;
+    replace->entries = (char *)entries;
+    replace->counters = counters;
+    replace->num_counters = 0;
+
+    for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+        /* Copy entries */
+
+        memcpy(entries, &chain->entries, sizeof(ebt_entries_t));
+
+        entries->distinguisher = 0;
+        entries->counter_offset = counter_offset;
+        entries->nentries = chain->rules.count;
+
+        /* Is chain a hook chain? */
+
+        if (chain->hookid != NF_BR_NUMHOOKS)
+            replace->hook_entry[chain->hookid] = entries;
+
+        /* Copy list of entries */
+
+        entries++;
+        entry = (ebt_entry_t *)entries;
+
+        for (rule = chain->rules.first; rule; rule = rule->next) {
+
+            size_entry = rule->entry->next_offset;
+
+            result = precommit_standard_target((*handle)->chains.first,
+                                               rule->entry);
+
+            if (result) {
+
+                free(entries);
+                free(replace);
+                ebtc_free(handle);
+
+                self_private.error_no = ERR_ENTRYTARGETINVALID;
+
+                return -1;
+
+            }
+
+            memcpy(entry, rule->entry, size_entry);
+            entry = (ebt_entry_t *)((char *)entry + size_entry);
+
+        }
+
+        /* Setup for next entries */
+
+        counter_offset += chain->rules.count;
+        entries = (ebt_entries_t *)entry;
+
+    }
+
+    entries = (ebt_entries_t *)replace->entries;
+
+    /* Create container for counter refresh */
+
+    if (size_counters > 0 && (*handle)->cache.num_counters > 0) {
+
+        size = (*handle)->cache.num_counters * sizeof(ebt_counter_t);
+        replace->num_counters = (*handle)->cache.num_counters;
+        counters_back = replace->counters = (ebt_counter_t *)malloc(size);
+
+        if (!counters_back) {
+
+            free(entries);
+            free(replace);
+            ebtc_free(handle);
+
+            self_private.error_no = ERR_ALLOCATEMEM;
+
+            return -1;
+
+        }
+
+    } else {
+
+        counters_back = replace->counters = NULL;
+        replace->num_counters = 0;
+
+    }
+
+    /* Submit entries to kernel */
+
+    fd = (*handle)->fd;
+
+    optlen = sizeof(ebt_replace_t);
+    optlen += size_entries;
+
+    if (setsockopt(fd, IPPROTO_IP, EBT_SO_SET_ENTRIES, replace, optlen)) {
+
+        if (counters_back)
+            free(counters_back);
+
+        free(entries);
+        free(replace);
+        ebtc_free(handle);
+
+        self_private.error_no = ERR_SETENTRIES;
+
+        return -1;
+
+    }
+
+    if (size_counters > 0) {
+
+        /* Allocate memory for counters */
+
+        replace->counters = counters = (ebt_counter_t *)malloc(size_counters);
+
+        if (!counters) {
+
+            if (counters_back)
+                free(counters_back);
+
+            free(replace);
+            ebtc_free(handle);
+
+            self_private.error_no = ERR_ALLOCATEMEM;
+
+            return -1;
+
+        }
+
+        /* Fill counters */
+
+        for (chain = (*handle)->chains.first; chain; chain = chain->next) {
+
+            for (rule = chain->rules.first; rule; rule = rule->next) {
+
+                if (rule->counter.changed == EBTC_FALSE) {
+
+                    memcpy(counters, &counters_back[rule->counter.offset],
+                           sizeof(ebt_counter_t));
+
+                } else {
+
+                    memcpy(counters, &rule->counter.counter,
+                           sizeof(ebt_counter_t));
+
+                }
+
+                counters++;
+
+            }
+
+        }
+
+        counters = replace->counters;
+
+        if (counters_back)
+            free(counters_back);
+
+        /* Submit counters to kernel */
+
+        replace->num_counters = count_entries;
+        replace->entries = NULL;
+
+        optlen = sizeof(ebt_replace_t);
+        optlen += sizeof(ebt_counter_t) * count_entries;
+
+        if (setsockopt(fd, IPPROTO_IP, EBT_SO_SET_COUNTERS, replace, optlen)) {
+
+            free(counters);
+            free(entries);
+            free(replace);
+            ebtc_free(handle);
+
+            self_private.error_no = ERR_SETCOUNTERS;
+
+            return -1;
+
+        }
+
+        free(counters);
+
+    }
+
+    /* Clean up */
+
+    free(entries);
+    free(replace);
+    ebtc_free(handle);
+
+    self_private.error_no = SUCCESS;
+
+    return 0;
+
+}
+
+
+void ebtc_free (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    ebt_replace_t           *replace = &(*handle)->replace;
+
+    chain_list_t            *chain = (*handle)->chains.first;
+
+    chain_list_t            *chain_next;
+
+    rule_list_t             *rule;
+
+    rule_list_t             *rule_next;
+
+
+
+/* ---- CODE ---- */
+
+    /* Free chain list with all rules */
+
+    for (; chain; chain = chain_next) {
+
+        for (rule = chain->rules.first; rule; rule = rule_next) {
+
+            rule_next = rule->next;
+            free(rule);
+
+        }
+
+        chain_next = chain->next;
+        free(chain);
+
+    }
+
+    /* Close gateway into the kernel */
+
+    close((*handle)->fd);
+
+    /* Clean up*/
+
+    if (replace->counters)
+        free(replace->counters);
+
+    if (replace->entries)
+        free(replace->entries);
+
+    free(*handle);
+
+    (*handle)->error_no = SUCCESS;
+
+    return;
+
+}
+
+
+const char *ebtc_strerror (const ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+
+
+/* ---- CODE ---- */
+
+    if (handle)
+        i = (*handle)->error_no;
+    else
+        i = self_private.error_no;
+
+    if (i > sizeof(error_msg) / sizeof(error_msg[0]))
+        return NULL;
+
+    return error_msg[i].msg;
+
+}
+
+
diff --git a/userspace/libebtc/test/Makefile.am b/userspace/libebtc/test/Makefile.am
new file mode 100644
index 0000000..201ff5e
--- /dev/null
+++ b/userspace/libebtc/test/Makefile.am
@@ -0,0 +1,51 @@
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+
+MAINTAINERCLEANFILES        = Makefile.in
+
+AM_CFLAGS                   = @CFLAGS@ -I../include
+AM_LDFLAGS                  = @LDFLAGS@
+
+EXTRA_DIST                  =
+
+noinst_PROGRAMS             = ebtc_test1 ebtc_test2
+
+ebtc_test1_SOURCES          = ../src/ebtc.c ebtc_test1.c util.c
+ebtc_test2_SOURCES          = ../src/ebtc.c ebtc_test2.c util.c
+
+
diff --git a/userspace/libebtc/test/Makefile.in b/userspace/libebtc/test/Makefile.in
new file mode 100644
index 0000000..dc12956
--- /dev/null
+++ b/userspace/libebtc/test/Makefile.in
@@ -0,0 +1,493 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# ==[ Makefile ]================================================================
+#
+#  Project
+#
+#      Library for ethernet bridge tables.
+#
+#
+#  Description
+#
+#      Process this file with automake to create Makefile.in
+#
+#
+#  Copyright
+#
+#      Copyright 2005 by Jens Götze
+#      All rights reserved.
+#
+#      This program is free software; you can redistribute it and/or modify
+#      it under the terms of the GNU General Public License as published by
+#      the Free Software Foundation; either version 2 of the License, or
+#      (at your option) any later version.
+#
+#      This program is distributed in the hope that it will be useful,
+#      but WITHOUT ANY WARRANTY; without even the implied warranty of
+#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#      GNU General Public License for more details.
+#
+#      You should have received a copy of the GNU General Public License
+#      along with this program; if not, write to the Free Software
+#      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+#      USA.
+#
+#
+# ==============================================================================
+#
+
+SOURCES = $(ebtc_test1_SOURCES) $(ebtc_test2_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = ebtc_test1$(EXEEXT) ebtc_test2$(EXEEXT)
+subdir = test
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_ebtc_test1_OBJECTS = ebtc.$(OBJEXT) ebtc_test1.$(OBJEXT) \
+	util.$(OBJEXT)
+ebtc_test1_OBJECTS = $(am_ebtc_test1_OBJECTS)
+ebtc_test1_LDADD = $(LDADD)
+am_ebtc_test2_OBJECTS = ebtc.$(OBJEXT) ebtc_test2.$(OBJEXT) \
+	util.$(OBJEXT)
+ebtc_test2_OBJECTS = $(am_ebtc_test2_OBJECTS)
+ebtc_test2_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(ebtc_test1_SOURCES) $(ebtc_test2_SOURCES)
+DIST_SOURCES = $(ebtc_test1_SOURCES) $(ebtc_test2_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+EBTC_LT_AGE = @EBTC_LT_AGE@
+EBTC_LT_CURRENT = @EBTC_LT_CURRENT@
+EBTC_LT_REVISION = @EBTC_LT_REVISION@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+MAINTAINERCLEANFILES = Makefile.in
+AM_CFLAGS = @CFLAGS@ -I../include
+AM_LDFLAGS = @LDFLAGS@
+EXTRA_DIST = 
+ebtc_test1_SOURCES = ../src/ebtc.c ebtc_test1.c util.c
+ebtc_test2_SOURCES = ../src/ebtc.c ebtc_test2.c util.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  test/Makefile'; \
+	cd $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu  test/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; for p in $$list; do \
+	  f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+	  echo " rm -f $$p $$f"; \
+	  rm -f $$p $$f ; \
+	done
+ebtc_test1$(EXEEXT): $(ebtc_test1_OBJECTS) $(ebtc_test1_DEPENDENCIES) 
+	@rm -f ebtc_test1$(EXEEXT)
+	$(LINK) $(ebtc_test1_LDFLAGS) $(ebtc_test1_OBJECTS) $(ebtc_test1_LDADD) $(LIBS)
+ebtc_test2$(EXEEXT): $(ebtc_test2_OBJECTS) $(ebtc_test2_DEPENDENCIES) 
+	@rm -f ebtc_test2$(EXEEXT)
+	$(LINK) $(ebtc_test2_LDFLAGS) $(ebtc_test2_OBJECTS) $(ebtc_test2_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ebtc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ebtc_test1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ebtc_test2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(LTCOMPILE) -c -o $@ $<
+
+ebtc.o: ../src/ebtc.c
+@am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ebtc.o -MD -MP -MF "$(DEPDIR)/ebtc.Tpo" -c -o ebtc.o `test -f '../src/ebtc.c' || echo '$(srcdir)/'`../src/ebtc.c; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ebtc.Tpo" "$(DEPDIR)/ebtc.Po"; else rm -f "$(DEPDIR)/ebtc.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='../src/ebtc.c' object='ebtc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ebtc.o `test -f '../src/ebtc.c' || echo '$(srcdir)/'`../src/ebtc.c
+
+ebtc.obj: ../src/ebtc.c
+@am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ebtc.obj -MD -MP -MF "$(DEPDIR)/ebtc.Tpo" -c -o ebtc.obj `if test -f '../src/ebtc.c'; then $(CYGPATH_W) '../src/ebtc.c'; else $(CYGPATH_W) '$(srcdir)/../src/ebtc.c'; fi`; \
+@am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ebtc.Tpo" "$(DEPDIR)/ebtc.Po"; else rm -f "$(DEPDIR)/ebtc.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='../src/ebtc.c' object='ebtc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ebtc.obj `if test -f '../src/ebtc.c'; then $(CYGPATH_W) '../src/ebtc.c'; else $(CYGPATH_W) '$(srcdir)/../src/ebtc.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	    $$tags $$unique; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(CTAGS_ARGS)$$tags$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$tags $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && cd $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+	list='$(DISTFILES)'; for file in $$list; do \
+	  case $$file in \
+	    $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+	    $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+	  esac; \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+	  if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+	    dir="/$$dir"; \
+	    $(mkdir_p) "$(distdir)$$dir"; \
+	  else \
+	    dir=''; \
+	  fi; \
+	  if test -d $$d/$$file; then \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+	    fi; \
+	    cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || cp -p $$d/$$file $(distdir)/$$file \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstPROGRAMS ctags distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-exec \
+	install-exec-am install-info install-info-am install-man \
+	install-strip installcheck installcheck-am installdirs \
+	maintainer-clean maintainer-clean-generic mostlyclean \
+	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+	pdf pdf-am ps ps-am tags uninstall uninstall-am \
+	uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/userspace/libebtc/test/ebtc_test1.c b/userspace/libebtc/test/ebtc_test1.c
new file mode 100644
index 0000000..5137adb
--- /dev/null
+++ b/userspace/libebtc/test/ebtc_test1.c
@@ -0,0 +1,616 @@
+/*
+ * ==[ FILENAME: ebtc_test.c ]==================================================
+ *
+ *  Project
+ *
+ *      Library for ethernet bridge tables.
+ *
+ *
+ *  Description
+ *
+ *      Test for this library.
+ *
+ *
+ *  Copyright
+ *
+ *      Copyright 2005 by Jens Götze
+ *      All rights reserved.
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+ *      USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <util.h>
+#include <libebtc.h>
+
+
+int insert_entry (const char *chain, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    unsigned int            size = 0;
+
+    struct ebt_entry        *entry;
+
+    struct ebt_standard_target
+                            *std_target;
+
+    struct ebt_entry_target *target;
+
+
+
+/* ---- CODE ---- */
+
+    /* Allocate memory */
+
+    size += EBTC_ALIGN(sizeof(struct ebt_entry));
+    size += EBTC_ALIGN(sizeof(struct ebt_standard_target));
+
+    entry = (struct ebt_entry *)malloc(size);
+
+    if (!entry) {
+
+        eprintf("Can't allocate memory\n");
+
+        return -1;
+
+    }
+
+    memset(entry, 0, size);
+
+    size = EBTC_ALIGN(sizeof(*entry));
+    std_target = (struct ebt_standard_target *)((char *)entry + size);
+
+    /* Prepare entry */
+
+    entry->bitmask = EBT_ENTRY_OR_ENTRIES | EBT_NOPROTO;
+
+    entry->watchers_offset = EBTC_ALIGN(sizeof(*entry));
+    entry->target_offset = entry->watchers_offset;
+    entry->next_offset = entry->target_offset + EBTC_ALIGN(sizeof(*std_target));
+
+    /* Prepare target */
+
+    target = &std_target->target;
+    target->target_size = EBTC_ALIGN(sizeof(*std_target));
+    target->target_size -= EBTC_ALIGN(sizeof(*target));
+
+    snprintf(target->u.name, EBT_FUNCTION_MAXNAMELEN, "standard");
+
+    std_target->verdict = EBT_CONTINUE;
+
+    /* Append */
+
+    if (ebtc_insert_entry(chain, entry, 0, handle)) {
+
+        eprintf("Can't insert entry\n");
+
+        free(entry);
+
+        return -1;
+
+    }
+
+    free(entry);
+
+    return 0;
+
+}
+
+
+int replace_entry (const char *chain, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    unsigned int            size = 0;
+
+    struct ebt_entry        *entry;
+
+    struct ebt_standard_target
+                            *std_target;
+
+    struct ebt_entry_target *target;
+
+
+
+/* ---- CODE ---- */
+
+    /* Allocate memory */
+
+    size += EBTC_ALIGN(sizeof(struct ebt_entry));
+    size += EBTC_ALIGN(sizeof(struct ebt_standard_target));
+
+    entry = (struct ebt_entry *)malloc(size);
+
+    if (!entry) {
+
+        eprintf("Can't allocate memory\n");
+
+        return -1;
+
+    }
+
+    memset(entry, 0, size);
+
+    size = EBTC_ALIGN(sizeof(*entry));
+    std_target = (struct ebt_standard_target *)((char *)entry + size);
+
+    /* Prepare entry */
+
+    entry->bitmask = EBT_ENTRY_OR_ENTRIES | EBT_NOPROTO;
+
+    entry->watchers_offset = EBTC_ALIGN(sizeof(*entry));
+    entry->target_offset = entry->watchers_offset;
+    entry->next_offset = entry->target_offset + EBTC_ALIGN(sizeof(*std_target));
+
+    /* Prepare target */
+
+    target = &std_target->target;
+    target->target_size = EBTC_ALIGN(sizeof(*std_target));
+    target->target_size -= EBTC_ALIGN(sizeof(*target));
+
+    snprintf(target->u.name, EBT_FUNCTION_MAXNAMELEN, "standard");
+
+    std_target->verdict = EBT_ACCEPT;
+
+    /* Append */
+
+    if (ebtc_replace_entry(chain, entry, 1, handle)) {
+
+        eprintf("Can't replace entry\n");
+
+        free(entry);
+
+        return -1;
+
+    }
+
+    free(entry);
+
+    return 0;
+
+}
+
+
+int append_entry (const char *chain, ebtc_handle_t *handle, char *chainname)
+{
+
+/* ---- VAR ---- */
+
+    unsigned int            size = 0;
+
+    struct ebt_entry        *entry;
+
+    struct ebt_standard_target
+                            *std_target;
+
+    struct ebt_entry_target *target;
+
+
+
+/* ---- CODE ---- */
+
+    /* Allocate memory */
+
+    size += EBTC_ALIGN(sizeof(struct ebt_entry));
+    size += EBTC_ALIGN(sizeof(struct ebt_standard_target));
+
+    entry = (struct ebt_entry *)malloc(size);
+
+    if (!entry) {
+
+        eprintf("Can't allocate memory\n");
+
+        return -1;
+
+    }
+
+    memset(entry, 0, size);
+
+    size = EBTC_ALIGN(sizeof(*entry));
+    std_target = (struct ebt_standard_target *)((char *)entry + size);
+
+    /* Prepare entry */
+
+    entry->bitmask = EBT_ENTRY_OR_ENTRIES | EBT_NOPROTO;
+
+    entry->watchers_offset = EBTC_ALIGN(sizeof(*entry));
+    entry->target_offset = entry->watchers_offset;
+    entry->next_offset = entry->target_offset + EBTC_ALIGN(sizeof(*std_target));
+
+    /* Prepare target */
+
+    if (ebtc_target_jumptochain(std_target, chainname, handle)) {
+
+        eprintf("Can't jump to chain\n");
+
+        return -1;
+
+    }
+
+    /* Append */
+
+    if (ebtc_append_entry(chain, entry, handle)) {
+
+        eprintf("Can't append entry\n");
+
+        free(entry);
+
+        return -1;
+
+    }
+
+    free(entry);
+
+    return 0;
+
+}
+
+
+int main ()
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    const char              *chain;
+
+    const char              *policy;
+
+    ebtc_handle_t           handle;
+
+    const struct ebt_entry  *rule;
+
+    struct ebt_counter      counter_new;
+
+    const struct ebt_counter
+                            *counter;
+
+
+
+/* ---- CODE ---- */
+
+    /* Open ebtables handle */
+
+    handle = ebtc_init("filter", EBTC_INIT_WITHFLUSH);
+
+    if (!handle) {
+
+        eprintf("Can't open ebtables (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    if (ebtc_commit(&handle)) {
+
+        eprintf("Can't commit ebtables changes\n");
+
+        return 1;
+
+    }
+
+    /* Open ebtables handle */
+
+    handle = ebtc_init("filter", EBTC_INIT_WITHFLUSH);
+
+    if (!handle) {
+
+        eprintf("Can't open ebtables (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    /* Entry functions */
+
+    if (append_entry("FORWARD", &handle, "CONTINUE")) {
+
+        eprintf("Can't append entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (append_entry("FORWARD", &handle, "CONTINUE")) {
+
+        eprintf("Can't append entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (insert_entry("FORWARD", &handle)) {
+
+        eprintf("Can't insert entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (replace_entry("FORWARD", &handle)) {
+
+        eprintf("Can't replace entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    /* Set policies */
+
+    policy = ebtc_get_policy("FORWARD", &handle);
+
+    if (!policy) {
+
+        eprintf("Can't get policy of chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    printf("Chain policy before change: %s\n", policy);
+
+    if (ebtc_set_policy("FORWARD", "DROP", &handle)) {
+
+        eprintf("Can't set policy\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    policy = ebtc_get_policy("FORWARD", &handle);
+
+    if (!policy) {
+
+        eprintf("Can't get policy of chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    printf("Chain policy after change:  %s\n", policy);
+    printf("\n");
+
+    /* Create chain */
+
+    if (ebtc_create_chain("test_prerename", &handle)) {
+
+        eprintf("Can't create chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (ebtc_rename_chain("test_prerename", "test", &handle)) {
+
+        eprintf("Can't rename chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    /* Delete chain */
+
+    printf("Chains before delete:\n");
+
+    chain = ebtc_first_chain(&handle);
+
+    while (chain) {
+
+        printf("  Chain: '%s'\n", chain);
+
+        chain = ebtc_next_chain(&handle);
+
+    }
+
+    if (ebtc_delete_chain("test", &handle)) {
+
+        eprintf("Can't delete chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    printf("Chains after delete:\n");
+
+    chain = ebtc_first_chain(&handle);
+
+    while (chain) {
+
+        printf("  Chain: '%s'\n", chain);
+
+        chain = ebtc_next_chain(&handle);
+
+    }
+
+    printf("\n");
+
+    /* Delete entry test */
+
+    if (append_entry("INPUT", &handle, "RETURN")) {
+
+        eprintf("Can't append entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    counter_new.bcnt = 1;
+    counter_new.pcnt = 2;
+
+    if (ebtc_set_counter("INPUT", 0, &counter_new, &handle)) {
+
+        eprintf("Can't set counter\n");
+
+        ebtc_free(&handle);
+
+        return -1;
+
+    }
+
+    if (insert_entry("INPUT", &handle)) {
+
+        eprintf("Can't insert entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    printf("Rule list in chain INPUT before delete\n");
+
+    i = 0;
+    rule = ebtc_first_rule("INPUT", &handle);
+
+    while (rule) {
+
+        counter = ebtc_read_counter("INPUT", i++, &handle);
+
+        printf("  Entry jump to '%s' (bcnt = %lld; pcnt = %lld)\n",
+               ebtc_get_target(rule, &handle), counter->bcnt, counter->pcnt);
+
+        rule = ebtc_next_rule("INPUT", &handle);
+
+    }
+
+    if (ebtc_delete_entry("INPUT", 0, &handle)) {
+
+        eprintf("Can't delete entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (ebtc_zero_counter("INPUT", 0, &handle)) {
+
+        eprintf("Can't zero counter\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    printf("Rule list in chain INPUT after delete\n");
+
+    i = 0;
+    rule = ebtc_first_rule("INPUT", &handle);
+
+    while (rule) {
+
+        counter = ebtc_read_counter("INPUT", i++, &handle);
+
+        printf("  Entry jump to '%s' (bcnt = %lld; pcnt = %lld)\n",
+               ebtc_get_target(rule, &handle), counter->bcnt, counter->pcnt);
+
+        rule = ebtc_next_rule("INPUT", &handle);
+
+    }
+
+    printf("\n");
+
+    /* Counter test */
+
+    if (append_entry("OUTPUT", &handle, "DROP")) {
+
+        eprintf("Can't append entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (ebtc_zero_entries("OUTPUT", &handle)) {
+
+        eprintf("Can't zero counter of chain\n");
+
+        return 1;
+
+    }
+
+    /* Jump to chain */
+
+    if (ebtc_create_chain("test2", &handle)) {
+
+        eprintf("Can't create chain\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    if (append_entry("OUTPUT", &handle, "test2")) {
+
+        eprintf("Can't append entry\n");
+
+        ebtc_free(&handle);
+
+        return 1;
+
+    }
+
+    /* Submit changes to kernel */
+
+    if (ebtc_commit(&handle)) {
+
+        eprintf("Can't commit ebtables changes (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    printf("Test successful finished.\n\n");
+
+    return 0;
+
+}
+
+
diff --git a/userspace/libebtc/test/ebtc_test2.c b/userspace/libebtc/test/ebtc_test2.c
new file mode 100644
index 0000000..98ac461
--- /dev/null
+++ b/userspace/libebtc/test/ebtc_test2.c
@@ -0,0 +1,342 @@
+/*
+ * ==[ FILENAME: ebtc_test2.c ]=================================================
+ *
+ *  Project
+ *
+ *      Library for ethernet bridge tables.
+ *
+ *
+ *  Description
+ *
+ *      Test for this library.
+ *
+ *
+ *  Copyright
+ *
+ *      Copyright 2005 by Jens Götze
+ *      All rights reserved.
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+ *      USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <util.h>
+#include <libebtc.h>
+
+
+int append_entry (const char *chain, ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    unsigned int            size = 0;
+
+    struct ebt_entry        *entry;
+
+    struct ebt_standard_target
+                            *std_target;
+
+    struct ebt_entry_target *target;
+
+
+
+/* ---- CODE ---- */
+
+    /* Allocate memory */
+
+    size += EBTC_ALIGN(sizeof(struct ebt_entry));
+    size += EBTC_ALIGN(sizeof(struct ebt_standard_target));
+
+    entry = (struct ebt_entry *)malloc(size);
+
+    if (!entry) {
+
+        eprintf("Can't allocate memory\n");
+
+        return -1;
+
+    }
+
+    memset(entry, 0, size);
+
+    size = EBTC_ALIGN(sizeof(*entry));
+    std_target = (struct ebt_standard_target *)((char *)entry + size);
+
+    /* Prepare entry */
+
+    entry->bitmask = EBT_ENTRY_OR_ENTRIES | EBT_NOPROTO;
+
+    entry->watchers_offset = EBTC_ALIGN(sizeof(*entry));
+    entry->target_offset = entry->watchers_offset;
+    entry->next_offset = entry->target_offset + EBTC_ALIGN(sizeof(*std_target));
+
+    /* Prepare target */
+
+    target = &std_target->target;
+    target->target_size = EBTC_ALIGN(sizeof(*std_target));
+    target->target_size -= EBTC_ALIGN(sizeof(*target));
+
+    snprintf(target->u.name, EBT_FUNCTION_MAXNAMELEN, "standard");
+
+    std_target->verdict = EBT_CONTINUE;
+
+    /* Append */
+
+    if (ebtc_append_entry(chain, entry, handle)) {
+
+        eprintf("Can't append entry\n");
+
+        free(entry);
+
+        return -1;
+
+    }
+
+    free(entry);
+
+    return 0;
+
+}
+
+
+void printlist (ebtc_handle_t *handle)
+{
+
+/* ---- VAR ---- */
+
+    const struct ebt_entry *rule;
+
+    int                     i = 0;
+
+    const struct ebt_counter
+                            *counter;
+
+
+
+/* ---- CODE ---- */
+
+    rule = ebtc_first_rule("INPUT", handle);
+
+    while (rule) {
+
+        counter = ebtc_read_counter("INPUT", i++, handle);
+
+        printf("  Entry jump to '%s' (bcnt = %lld; pcnt = %lld)\n",
+               ebtc_get_target(rule, handle), counter->bcnt, counter->pcnt);
+
+        rule = ebtc_next_rule("INPUT", handle);
+
+    }
+
+}
+
+
+int test_run ()
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    const char              *chain;
+
+    const char              *policy;
+
+    ebtc_handle_t           handle;
+
+
+
+/* ---- CODE ---- */
+
+    /* Open ebtables handle */
+
+    handle = ebtc_init("filter", EBTC_INIT);
+
+    if (!handle) {
+
+        eprintf("Can't open ebtables (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    /* Delete and append entry */
+
+    printf("Rule list in chain INPUT before delete\n");
+    printlist(&handle);
+    printf("\n");
+
+    printf("Delete second entry\n");
+
+    if (ebtc_delete_entry("INPUT", 1, &handle)) {
+
+        eprintf("Can't delete entry\n");
+
+        return 1;
+
+    }
+
+    printf("\n");
+
+    printf("Rule list in chain INPUT after delete\n");
+    printlist(&handle);
+    printf("\n");
+
+    printf("Append entry\n");
+
+    if (append_entry("INPUT", &handle)) {
+
+        eprintf("Can't append entry\n");
+
+        return 1;
+
+    }
+
+    printf("\n");
+
+    /* Wait for packets */
+
+    printf("Wait 5 seconds for network traffic...\n");
+    sleep(5);
+    printf("\n");
+
+    /* Submit changes to kernel */
+
+    if (ebtc_commit(&handle)) {
+
+        eprintf("Can't commit ebtables changes\n");
+
+        return 1;
+
+    }
+
+    /* Open ebtables handle */
+
+    handle = ebtc_init("filter", EBTC_INIT);
+
+    if (!handle) {
+
+        eprintf("Can't open ebtables (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    /* List current entries */
+
+    printf("Rule list in chain INPUT after commit\n");
+    printlist(&handle);
+    printf("\n");
+
+    /* Submit changes to kernel */
+
+    ebtc_free(&handle);
+
+    return 0;
+
+}
+
+
+int test_init ()
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    const char              *chain;
+
+    const char              *policy;
+
+    ebtc_handle_t           handle;
+
+
+
+/* ---- CODE ---- */
+
+    handle = ebtc_init("filter", EBTC_INIT_WITHFLUSH);
+
+    if (!handle) {
+
+        eprintf("Can't open ebtables (%s)\n", ebtc_strerror(NULL));
+
+        return 1;
+
+    }
+
+    printf("Append entry\n");
+
+    if (append_entry("INPUT", &handle)) {
+
+        eprintf("Can't append entry\n");
+
+        return 1;
+
+    }
+
+    printf("Append entry\n");
+
+    if (append_entry("INPUT", &handle)) {
+
+        eprintf("Can't append entry\n");
+
+        return 1;
+
+    }
+
+    printf("\n");
+
+    if (ebtc_commit(&handle)) {
+
+        eprintf("Can't commit ebtables changes\n");
+
+        return 1;
+
+    }
+
+    return 0;
+    
+}
+
+
+int main (int argc, char **argv)
+{
+
+/* ---- CODE ---- */
+
+    if (argc != 2) {
+
+        printf("Usage: progname <init|run>\n\n");
+
+        return 1;
+
+    }
+
+    if (!strcmp(argv[1], "init"))
+        return test_init();
+    else if (!strcmp(argv[1], "run"))
+        return test_run();
+
+    return 0;
+
+}
+
+
diff --git a/userspace/libebtc/test/util.c b/userspace/libebtc/test/util.c
new file mode 100644
index 0000000..cb18eb5
--- /dev/null
+++ b/userspace/libebtc/test/util.c
@@ -0,0 +1,137 @@
+/*
+ * ==[ FILENAME: util.c ]=======================================================
+ *
+ *  Project
+ *
+ *      Library for ethernet bridge tables.
+ *
+ *
+ *  Description
+ *
+ *      Utilities
+ *
+ *
+ *  Copyright
+ *
+ *      Copyright 2005 by Jens Götze
+ *      All rights reserved.
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,
+ *      USA.
+ *
+ *
+ * =============================================================================
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+
+
+int __eprintf (char *filename, int line, char *format, ...)
+{
+
+/* ---- VAR ---- */
+
+    va_list                 ap;
+
+
+
+/* ---- CODE ---- */
+
+    /* Open argument list */
+
+    va_start(ap, format);
+
+    /* Print message on screen */
+
+    fprintf(stderr, "[ERROR] [%s:%d] ", filename, line);
+    vfprintf(stderr, format, ap);
+    fflush(stderr);
+
+    /* Close argument list */
+
+    va_end(ap);
+
+    return 0;
+
+}
+
+
+void printhex (FILE *output, const char *buf, size_t size)
+{
+
+/* ---- VAR ---- */
+
+    int                     i;
+
+    char                    *ptr;
+
+    char                    c;
+
+    size_t                  tmp;
+
+
+
+/* ---- CODE ---- */
+
+    while (size) {
+
+        if (size > 1) {
+
+            fprintf(output, "%08X-%08X ", (unsigned int)buf,
+                    (unsigned int)buf + (size > 15 ? 15 : size - 1));
+
+        } else
+            fprintf(output, "%08X %8.8s ", (unsigned int)buf, "");
+
+        ptr = (char *)buf;
+        tmp = size;
+
+        for (i = 8; i && tmp; i--, tmp--)
+            fprintf(output, " %02X", (unsigned char)*ptr++);
+
+        for (; i; i--)
+            fprintf(output, "   ");
+
+        fprintf(output, " ");
+
+        if (tmp) {
+
+            for (i = 8; i && tmp; i--, tmp--)
+                fprintf(output, " %02X", (unsigned char)*ptr++);
+
+        } else
+            i = 8;
+
+        for (; i; i--)
+            fprintf(output, "   ");
+
+        fprintf(output, "  ");
+
+        for (i = 16; i && size; i--, size--) {
+
+            c = *buf++;
+            fprintf(output, "%c", c < 32 ? '.' : c);
+
+        }
+
+        fprintf(output, "\n");
+
+    }
+
+}
+
+
diff --git a/userspace/patches/arptables/zipped/arptables-v0.0.0.tar.gz b/userspace/patches/arptables/zipped/arptables-v0.0.0.tar.gz
new file mode 100644
index 0000000..ce45f51
--- /dev/null
+++ b/userspace/patches/arptables/zipped/arptables-v0.0.0.tar.gz
Binary files differ
diff --git a/userspace/patches/arptables/zipped/arptables-v0.0.2.tar.gz b/userspace/patches/arptables/zipped/arptables-v0.0.2.tar.gz
new file mode 100644
index 0000000..596ba77
--- /dev/null
+++ b/userspace/patches/arptables/zipped/arptables-v0.0.2.tar.gz
Binary files differ
diff --git a/userspace/patches/arptables/zipped/arptables-v0.0.3-2.tar.gz b/userspace/patches/arptables/zipped/arptables-v0.0.3-2.tar.gz
new file mode 100644
index 0000000..e21e702
--- /dev/null
+++ b/userspace/patches/arptables/zipped/arptables-v0.0.3-2.tar.gz
Binary files differ
diff --git a/userspace/patches/arptables/zipped/arptables-v0.0.3.tar.gz b/userspace/patches/arptables/zipped/arptables-v0.0.3.tar.gz
new file mode 100644
index 0000000..61968cb
--- /dev/null
+++ b/userspace/patches/arptables/zipped/arptables-v0.0.3.tar.gz
Binary files differ
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0-rc1.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0-rc1.001.diff
new file mode 100644
index 0000000..b6ee40f
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0-rc1.001.diff
@@ -0,0 +1,1764 @@
+--- ebtables-v2.0pre10/Makefile	Wed Jul 10 22:12:36 2002
++++ ebtables-v2.0-rc1.001/Makefile	Wed Jul 31 19:48:25 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre10 (July 2002)"
++PROGVERSION:="2.0-rc1 (July 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre10/ebtables.c	Tue Jul 16 20:36:50 2002
++++ ebtables-v2.0-rc1.001/ebtables.c	Thu Jul 25 16:02:43 2002
+@@ -1,5 +1,5 @@
+ /*
+- * ebtables.c, v2.0 April 2002
++ * ebtables.c, v2.0 July 2002
+  *
+  * Author: Bart De Schuymer
+  *
+@@ -32,13 +32,12 @@
+ #include <linux/br_db.h> // the database
+ #include <netinet/in.h>
+ #include <netinet/ether.h>
+-#include <asm/types.h>
+ #include "include/ebtables_u.h"
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+ 
+-// here are the number-name correspondences kept for the ethernet
++// here are the number-name correspondences kept for the Ethernet
+ // frame type field
+ #define PROTOCOLFILE "/etc/ethertypes"
+ 
+@@ -123,8 +122,6 @@
+ unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+ unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+ 
+-// tells what happened to the old rules
+-static unsigned short *counterchanges;
+ // holds all the data
+ static struct ebt_u_replace replace;
+ 
+@@ -228,8 +225,7 @@
+ 	struct ebt_u_match_list **m_list, *new;
+ 
+ 	m->used = 1;
+-	for (m_list = &new_entry->m_list;
+-	*m_list; m_list = &(*m_list)->next);
++	for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+ 	new = (struct ebt_u_match_list *)
+ 	   malloc(sizeof(struct ebt_u_match_list));
+ 	if (!new)
+@@ -245,8 +241,7 @@
+ 	struct ebt_u_watcher_list *new;
+ 
+ 	w->used = 1;
+-	for (w_list = &new_entry->w_list;
+-	   *w_list; w_list = &(*w_list)->next);
++	for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+ 	new = (struct ebt_u_watcher_list *)
+ 	   malloc(sizeof(struct ebt_u_watcher_list));
+ 	if (!new)
+@@ -340,6 +335,7 @@
+ 	ebt_options = merge_options
+ 	   (ebt_options, t->extra_ops, &(t->option_offset));
+ 	t->init(t->t);
++
+ 	for (i = &targets; *i; i = &((*i)->next));
+ 	t->next = NULL;
+ 	*i = t;
+@@ -379,7 +375,6 @@
+ 	return NULL;
+ }
+ 
+-// I hate stealing, really... Lets call it a tribute.
+ int ebtables_insmod(const char *modname, const char *modprobe)
+ {
+ 	char *buf = NULL;
+@@ -413,86 +408,26 @@
+ 	return 0;
+ }
+ 
+-
+-// used to parse /etc/ethertypes
+-static int disregard_whitespace(char *buffer, FILE *ifp)
+-{
+-	int hlp;
+-
+-	buffer[0] = '\t';
+-	while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+-		hlp = fscanf(ifp, "%c", buffer);
+-		if (hlp == EOF || hlp == 0) return -1;
+-	}
+-	return 0;
+-}
+-
+-// used to parse /etc/ethertypes
+-static int disregard_tabspace(char *buffer, FILE *ifp)
+-{
+-	int hlp;
+-
+-	buffer[0] = '\t';
+-	while (buffer[0] == '\t' || buffer[0] == ' ') {
+-		hlp = fscanf(ifp, "%c", buffer);
+-		if (hlp == EOF || hlp == 0) return -1;
+-	}
+-	return 0;
+-}
+-
+ // helper function: processes a line of data from the file /etc/ethertypes
+ static int get_a_line(char *buffer, char *value, FILE *ifp)
+ {
+-	int i, hlp;
+-	char anotherhlp;
++	char line[80], *p;
++	const char delim[] = " \t\n";
+ 
+-	// discard comment lines and whitespace
+-	while (1) {
+-		if (disregard_whitespace(buffer, ifp))
+-			return -1;
+-		if (buffer[0] == '#')
+-			while (1) {
+-				hlp = fscanf(ifp, "%c", &anotherhlp);
+-				if (!hlp || hlp == EOF)
+-					return -1;
+-				if (anotherhlp == '\n')
+-					break;
+-			}
+-		else
+-			break;
+-	}
+-
+-	// buffer[0] already contains the first letter
+-	for (i = 1; i < 21; i++) {
+-		hlp = fscanf(ifp, "%c", buffer + i);
+-		if (hlp == EOF || hlp == 0)
+-			return -1;
+-		if (buffer[i] == '\t' || buffer[i] == ' ')
+-			break;
+-	}
+-	if (i == 21)
+-		return -1;
+-	buffer[i] = '\0';
+-	if (disregard_tabspace(value, ifp))
+-		return -1;
+-	// maybe I should allow 0x0800 instead of 0800, but I'm feeling lazy
+-	// buffer[0] already contains the first letter
+-	for (i = 1; i < 5; i++) {
+-		hlp = fscanf(ifp, "%c", value+i);
+-		if (value[i] == '\n' || value[i] == '\t' ||
+-		   value[i] == ' ' || hlp == EOF)
+-			break;
++	while (fgets(line, sizeof(line), ifp)) {
++		p = strtok(line, delim);
++		if (!p || p[0] == '#')
++			continue;
++		if (strlen(p) > 20)
++			continue;
++		strcpy(buffer, p);
++		p = strtok(NULL, delim);
++		if (!p || strlen(p) > 10)
++			continue;
++		strcpy(value, p);
++		return 0;
+ 	}
+-	if (i == 5) return -1;
+-	// discard comments at the end of a line
+-	if (value[i] == '\t' || value[i] == ' ')
+-		while (1) {
+-			hlp = fscanf(ifp, "%c", &anotherhlp);
+-			if (!hlp || hlp == EOF || anotherhlp == '\n')
+-				break;
+-		}
+-	value[i] = '\0';
+-	return 0;
++	return -1;
+ }
+ 
+ // translate a hexadecimal number to a protocol name, parsing /etc/ethertypes
+@@ -500,7 +435,7 @@
+ int number_to_name(unsigned short proto, char *name)
+ {
+ 	FILE *ifp;
+-	char buffer[21], value[5], *bfr;
++	char buffer[21], value[11], *bfr;
+ 	unsigned short i;
+ 
+ 	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+@@ -768,7 +703,9 @@
+ 		if (!(replace.valid_hooks & (1 << i)))
+ 			continue;
+ 		entries = nr_to_chain(i);
+-		entries->hook_mask = (1 << i);
++		// (1 << NF_BR_NUMHOOKS) implies it's a standard chain
++		// (usefull in the final_check() funtions)
++		entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
+ 		chain_nr = i;
+ 
+ 		e = entries->entries;
+@@ -784,7 +721,8 @@
+ 			for (k = 0; k < sp; k++)
+ 				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS)
+ 					print_error("Loop from chain %s to chain %s",
+-					   nr_to_chain(chain_nr)->name, nr_to_chain(stack[k].chain_nr)->name);
++					   nr_to_chain(chain_nr)->name,
++					   nr_to_chain(stack[k].chain_nr)->name);
+ 			// jump to the chain, make sure we know how to get back
+ 			stack[sp].chain_nr = chain_nr;
+ 			stack[sp].n = j;
+@@ -960,16 +898,16 @@
+ 		replace.num_counters = replace.nentries;
+ 		if (replace.nentries) {
+ 			// '+ 1' for the CNT_END
+-			if (!(counterchanges = (unsigned short *) malloc(
++			if (!(replace.counterchanges = (unsigned short *) malloc(
+ 			   (replace.nentries + 1) * sizeof(unsigned short))))
+ 				print_memory();
+ 			// done nothing special to the rules
+ 			for (i = 0; i < replace.nentries; i++)
+-				counterchanges[i] = CNT_NORM;
+-			counterchanges[replace.nentries] = CNT_END;
++				replace.counterchanges[i] = CNT_NORM;
++			replace.counterchanges[replace.nentries] = CNT_END;
+ 		}
+ 		else
+-			counterchanges = NULL;
++			replace.counterchanges = NULL;
+ 	}
+ 	else
+ 		exit(0);
+@@ -1026,14 +964,14 @@
+ 
+ 	if (replace.nentries) {
+ 		// +1 for CNT_END
+-		if ( !(counterchanges = (unsigned short *)
++		if ( !(replace.counterchanges = (unsigned short *)
+ 		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+ 	}
+ 	// delete the counters belonging to the specified chain,
+ 	// update counter_offset
+ 	i = -1;
+-	cnt = counterchanges;
++	cnt = replace.counterchanges;
+ 	while (1) {
+ 		i++;
+ 		entries = nr_to_chain(i);
+@@ -1101,73 +1039,74 @@
+ 	for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+ 		if (!u_e)
+ 			print_bug("Hmm, trouble");
+-		if ( u_e->ethproto == new_entry->ethproto
+-		   && !strcmp(u_e->in, new_entry->in)
+-		   && !strcmp(u_e->out, new_entry->out)) {
+-		   	if (strcmp(u_e->logical_in, new_entry->logical_in) ||
+-			   strcmp(u_e->logical_out, new_entry->logical_out))
+-				continue;
+-			if (new_entry->bitmask & EBT_SOURCEMAC &&
+-			   memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+-				continue;
+-			if (new_entry->bitmask & EBT_DESTMAC &&
+-			   memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+-				continue;
+-			if (new_entry->bitmask != u_e->bitmask ||
+-			   new_entry->invflags != u_e->invflags)
+-				continue;
+-			// compare all matches
+-			m_l = new_entry->m_list;
+-			j = 0;
+-			while (m_l) {
+-				m = (struct ebt_u_match *)(m_l->m);
+-				m_l2 = u_e->m_list;
+-				while (m_l2 &&
+-				   strcmp(m_l2->m->u.name, m->m->u.name))
+-					m_l2 = m_l2->next;
+-				if (!m_l2 || !m->compare(m->m, m_l2->m))
+-					goto letscontinue;
+-				j++;
+-				m_l = m_l->next;
+-			}
+-			// now be sure they have the same nr of matches
+-			k = 0;
+-			m_l = u_e->m_list;
+-			while (m_l) {
+-				k++;
+-				m_l = m_l->next;
+-			}
+-			if (j != k)
+-				continue;
++		if (u_e->ethproto != new_entry->ethproto)
++			continue;
++		if (strcmp(u_e->in, new_entry->in))
++			continue;
++		if (strcmp(u_e->out, new_entry->out))
++			continue;
++		if (strcmp(u_e->logical_in, new_entry->logical_in))
++			continue;
++		if (strcmp(u_e->logical_out, new_entry->logical_out))
++			continue;
++		if (new_entry->bitmask & EBT_SOURCEMAC &&
++		   memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
++			continue;
++		if (new_entry->bitmask & EBT_DESTMAC &&
++		   memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
++			continue;
++		if (new_entry->bitmask != u_e->bitmask ||
++		   new_entry->invflags != u_e->invflags)
++			continue;
++		// compare all matches
++		m_l = new_entry->m_list;
++		j = 0;
++		while (m_l) {
++			m = (struct ebt_u_match *)(m_l->m);
++			m_l2 = u_e->m_list;
++			while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
++				m_l2 = m_l2->next;
++			if (!m_l2 || !m->compare(m->m, m_l2->m))
++				goto letscontinue;
++			j++;
++			m_l = m_l->next;
++		}
++		// now be sure they have the same nr of matches
++		k = 0;
++		m_l = u_e->m_list;
++		while (m_l) {
++			k++;
++			m_l = m_l->next;
++		}
++		if (j != k)
++			continue;
+ 
+-			// compare all watchers
+-			w_l = new_entry->w_list;
+-			j = 0;
+-			while (w_l) {
+-				w = (struct ebt_u_watcher *)(w_l->w);
+-				w_l2 = u_e->w_list;
+-				while (w_l2 &&
+-				   strcmp(w_l2->w->u.name, w->w->u.name))
+-					w_l2 = w_l2->next;
+-				if (!w_l2 || !w->compare(w->w, w_l2->w))
+-					goto letscontinue;
+-				j++;
+-				w_l = w_l->next;
+-			}
+-			k = 0;
+-			w_l = u_e->w_list;
+-			while (w_l) {
+-				k++;
+-				w_l = w_l->next;
+-			}
+-			if (j != k)
+-				continue;
+-			if (strcmp(t->t->u.name, u_e->t->u.name))
+-				continue;
+-			if (!t->compare(t->t, u_e->t))
+-				continue;
+-			return i;
++		// compare all watchers
++		w_l = new_entry->w_list;
++		j = 0;
++		while (w_l) {
++			w = (struct ebt_u_watcher *)(w_l->w);
++			w_l2 = u_e->w_list;
++			while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
++				w_l2 = w_l2->next;
++			if (!w_l2 || !w->compare(w->w, w_l2->w))
++				goto letscontinue;
++			j++;
++			w_l = w_l->next;
++		}
++		k = 0;
++		w_l = u_e->w_list;
++		while (w_l) {
++			k++;
++			w_l = w_l->next;
+ 		}
++		if (j != k)
++			continue;
++		if (strcmp(t->t->u.name, u_e->t->u.name))
++			continue;
++		if (!t->compare(t->t, u_e->t))
++			continue;
++		return i;
+ letscontinue:
+ 	}
+ 	return -1;
+@@ -1177,7 +1116,7 @@
+ static void add_rule(int rule_nr)
+ {
+ 	int i, j;
+-	struct ebt_u_entry *u_e, *u_e2;
++	struct ebt_u_entry **u_e;
+ 	unsigned short *cnt;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+@@ -1196,10 +1135,10 @@
+ 
+ 	// handle counter stuff
+ 	// +1 for CNT_END
+-	if ( !(counterchanges = (unsigned short *)
++	if ( !(replace.counterchanges = (unsigned short *)
+ 	   malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+ 		print_memory();
+-	cnt = counterchanges;
++	cnt = replace.counterchanges;
+ 	for (i = 0; i < replace.selected_hook; i++) {
+ 		if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
+ 			continue;
+@@ -1215,25 +1154,19 @@
+ 	}
+ 	*cnt = CNT_ADD;
+ 	cnt++;
+-	while (cnt != counterchanges + replace.nentries) {
++	while (cnt != replace.counterchanges + replace.nentries) {
+ 		*cnt = CNT_NORM;
+ 		cnt++;
+ 	}
+ 	*cnt = CNT_END;
+ 
+ 	// go to the right position in the chain
+-	u_e2 = NULL;
+-	u_e = entries->entries;
+-	for (i = 0; i < rule_nr; i++) {
+-		u_e2 = u_e;
+-		u_e = u_e->next;
+-	}
++	u_e = &entries->entries;
++	for (i = 0; i < rule_nr; i++)
++		u_e = &(*u_e)->next;
+ 	// insert the rule
+-	if (u_e2)
+-		u_e2->next = new_entry;
+-	else
+-		entries->entries = new_entry;
+-	new_entry->next = u_e;
++	new_entry->next = *u_e;
++	*u_e = new_entry;
+ 
+ 	// put the ebt_[match, watcher, target] pointers in place
+ 	m_l = new_entry->m_list;
+@@ -1268,7 +1201,7 @@
+ {
+ 	int i, j, lentmp = 0;
+ 	unsigned short *cnt;
+-	struct ebt_u_entry *u_e, *u_e2;
++	struct ebt_u_entry **u_e, *u_e2;
+ 	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+ 	if ( (i = check_rule_exists(rule_nr)) == -1 )
+@@ -1277,6 +1210,7 @@
+ 	// we're deleting a rule
+ 	replace.num_counters = replace.nentries;
+ 	replace.nentries--;
++	entries->nentries--;
+ 
+ 	if (replace.nentries) {
+ 		for (j = 0; j < replace.selected_hook; j++) {
+@@ -1288,10 +1222,10 @@
+ 		}
+ 		lentmp += i;
+ 		// +1 for CNT_END
+-		if ( !(counterchanges = (unsigned short *)malloc(
++		if ( !(replace.counterchanges = (unsigned short *)malloc(
+ 		   (replace.num_counters + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+-		cnt = counterchanges;
++		cnt = replace.counterchanges;
+ 		for (j = 0; j < lentmp; j++) {
+ 			*cnt = CNT_NORM;
+ 			cnt++;
+@@ -1308,23 +1242,16 @@
+ 		replace.num_counters = 0;
+ 
+ 	// go to the right position in the chain
+-	u_e2 = NULL;
+-	u_e = entries->entries;
+-	for (j = 0; j < i; j++) {
+-		u_e2 = u_e;
+-		u_e = u_e->next;
+-	}
+-
+-	// remove from the chain
+-	if (u_e2)
+-		u_e2->next = u_e->next;
+-	else
+-		entries->entries = u_e->next;
+-
+-	entries->nentries--;
++	u_e = &entries->entries;
++	for (j = 0; j < i; j++)
++		u_e = &(*u_e)->next;
++	// remove the rule
++	u_e2 = *u_e;
++	*u_e = (*u_e)->next;
+ 	// free everything
+-	free_u_entry(u_e);
+-	free(u_e);
++	free_u_entry(u_e2);
++	free(u_e2);
++
+ 	// update the counter_offset of chains behind this one
+ 	i = replace.selected_hook;
+ 	while (1) {
+@@ -1348,7 +1275,7 @@
+ 		// tell main() we don't update the counters
+ 		// this results in tricking the kernel to zero its counters,
+ 		// naively expecting userspace to update its counters. Muahahaha
+-		counterchanges = NULL;
++		replace.counterchanges = NULL;
+ 		replace.num_counters = 0;
+ 	} else {
+ 		int i, j;
+@@ -1357,11 +1284,11 @@
+ 
+ 		if (entries->nentries == 0)
+ 			exit(0);
+-		counterchanges = (unsigned short *)
++		replace.counterchanges = (unsigned short *)
+ 		   malloc((replace.nentries + 1) * sizeof(unsigned short));
+-		if (!counterchanges)
++		if (!replace.counterchanges)
+ 			print_memory();
+-		cnt = counterchanges;
++		cnt = replace.counterchanges;
+ 		for (i = 0; i < zerochain; i++) {
+ 			if (i < NF_BR_NUMHOOKS &&
+ 			   !(replace.valid_hooks & (1 << i)))
+@@ -1376,7 +1303,7 @@
+ 			*cnt = CNT_ZERO;
+ 			cnt++;
+ 		}
+-		while (cnt != counterchanges + replace.nentries) {
++		while (cnt != replace.counterchanges + replace.nentries) {
+ 			*cnt = CNT_NORM;
+ 			cnt++;
+ 		}
+@@ -1450,7 +1377,7 @@
+ int name_to_number(char *name, __u16 *proto)
+ {
+ 	FILE *ifp;
+-	char buffer[21], value[5], *bfr;
++	char buffer[21], value[11], *bfr;
+ 	unsigned short i;
+ 
+ 	if (!strcasecmp("LENGTH", name)) {
+@@ -1461,14 +1388,15 @@
+ 	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+ 		return -1;
+ 	while (1) {
+-		if (get_a_line(buffer, value, ifp)) return -1;
++		if (get_a_line(buffer, value, ifp))
++			return -1;
+ 		if (strcasecmp(buffer, name))
+ 			continue;
++		fclose(ifp);
+ 		i = (unsigned short) strtol(value, &bfr, 16);
+ 		if (*bfr != '\0')
+ 			return -1;
+ 		*proto = i;
+-		fclose(ifp);
+ 		return 0;
+ 	}
+ 	return -1;
+@@ -1587,6 +1515,19 @@
+ 	*flags |= mask;
+ }
+ 
++static void get_kernel_table(const char *modprobe)
++{
++	if ( !(table = find_table(replace.name)) )
++		print_error("Bad table name");
++	// get the kernel's information
++	if (get_table(&replace)) {
++		ebtables_insmod("ebtables", modprobe);
++		if (get_table(&replace))
++			print_error("The kernel doesn't support the ebtables "
++			"%s table", replace.name);
++	}
++}
++
+ #define OPT_COMMAND    0x01
+ #define OPT_TABLE      0x02
+ #define OPT_IN         0x04
+@@ -1606,7 +1547,7 @@
+ 	// this special one for the -Z option (we can have -Z <this> -L <that>)
+ 	int zerochain = -1;
+ 	int policy = 0;
+-	int rule_nr = -1;// used for -D chain number
++	int rule_nr = -1;// used for -[D,I] chain number
+ 	struct ebt_u_target *t;
+ 	struct ebt_u_match *m;
+ 	struct ebt_u_watcher *w;
+@@ -1621,6 +1562,7 @@
+ 	replace.selected_hook = -1;
+ 	replace.command = 'h';
+ 	replace.filename = NULL;
++	replace.counterchanges = NULL;
+ 
+ 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ 	if (!new_entry)
+@@ -1648,16 +1590,8 @@
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
+-			if ( !(table = find_table(replace.name)) )
+-				print_error("Bad table name");
+-			// get the kernel's information
+-			if (get_table(&replace)) {
+-				ebtables_insmod("ebtables", modprobe);
+-				if (get_table(&replace))
+-					print_error("can't initialize ebtables "
+-					"table %s", replace.name);
+-			}
+-			if (optarg[0] == '-')
++			get_kernel_table(modprobe);
++			if (optarg[0] == '-' || !strcmp(optarg, "!"))
+ 				print_error("No chain name specified");
+ 			if (c == 'N') {
+ 				struct ebt_u_chain_list *cl, **cl2;
+@@ -1697,7 +1631,8 @@
+ 			if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+ 				print_error("Chain %s doesn't exist", optarg);
+ 			if (c == 'E') {
+-				if (optind >= argc || argv[optind][0] == '-')
++				if (optind >= argc || argv[optind][0] == '-' ||
++				   !strcmp(argv[optind], "!"))
+ 					print_error("No new chain name specified");
+ 				if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
+ 					print_error("Chain name len can't exceed %d",
+@@ -1705,6 +1640,9 @@
+ 				if (get_hooknr(argv[optind]) != -1)
+ 					print_error("Chain %s already exists",
+ 					   argv[optind]);
++				if (find_target(argv[optind]))
++					print_error("Target with name %s exists"
++					   , argv[optind]);
+ 				entries = to_chain();
+ 				strcpy(entries->name, argv[optind]);
+ 				optind++;
+@@ -1719,25 +1657,21 @@
+ 				check_for_references(replace.selected_hook - NF_BR_NUMHOOKS);
+ 				flush_chains();
+ 				entries = to_chain();
+-				if (replace.udc->udc == entries) {
+-					cl = replace.udc;
+-					replace.udc = replace.udc->next;
+-					free(cl->udc);
+-					free(cl);
+-					break;
+-				}
+ 				cl2 = &(replace.udc);
+-				while ((*cl2)->next->udc != entries)
++				while ((*cl2)->udc != entries)
+ 					cl2 = &((*cl2)->next);
+-				cl = (*cl2)->next;
+-				(*cl2)->next = (*cl2)->next->next;
++				cl = (*cl2);
++				(*cl2) = (*cl2)->next;
+ 				free(cl->udc);
+ 				free(cl);
+ 				break;
+ 			}
+ 
+-			if (c == 'D' && optind < argc &&
+-			   argv[optind][0] != '-') {
++			if ( (c == 'D' && optind < argc  &&
++			   argv[optind][0] != '-')  || c == 'I') {
++				if (optind >= argc || argv[optind][0] == '-')
++					print_error("No rulenr for -I"
++					            " specified");
+ 				rule_nr = strtol(argv[optind], &buffer, 10);
+ 				if (*buffer != '\0' || rule_nr < 0)
+ 					print_error("Problem with the "
+@@ -1760,16 +1694,6 @@
+ 					print_error("Wrong policy");
+ 				optind++;
+ 			}
+-			if (c == 'I') {
+-				if (optind >= argc)
+-					print_error("No rulenr for -I"
+-					            " specified");
+-				rule_nr = strtol(argv[optind], &buffer, 10);
+-				if (*buffer != '\0' || rule_nr < 0)
+-					print_error("Problem with the specified"
+-					            " rule number");
+-				optind++;
+-			}
+ 			break;
+ 
+ 		case 'L': // list
+@@ -1791,23 +1715,14 @@
+ 					            " not allowed");
+ 				replace.flags |= OPT_COMMAND;
+ 			}
+-			if ( !(table = find_table(replace.name)) )
+-				print_error("Bad table name");
+-			// get the kernel's information
+-			if (get_table(&replace)) {
+-				ebtables_insmod("ebtables", modprobe);
+-				if (get_table(&replace))
+-					print_error("can't initialize ebtables "
+-					"table %s", replace.name);
+-			}
++			get_kernel_table(modprobe);
+ 			i = -1;
+ 			if (optarg) {
+ 				if ( (i = get_hooknr(optarg)) == -1 )
+ 					print_error("Bad chain");
+ 			} else
+ 				if (optind < argc && argv[optind][0] != '-') {
+-					if ((i = get_hooknr(argv[optind]))
+-					   == -1)
++					if ((i = get_hooknr(argv[optind])) == -1)
+ 						print_error("Bad chain");
+ 					optind++;
+ 				}
+@@ -2054,7 +1969,7 @@
+ 			if (new_entry->ethproto < 1536 &&
+ 			   !(new_entry->bitmask & EBT_802_3))
+ 				print_error("Sorry, protocols have values above"
+-				            " or equal to 1536 (0x0600)");
++				            " or equal to 0x0600");
+ 			break;
+ 
+ 		case 'b': // allow database?
+@@ -2104,17 +2019,24 @@
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
++			if (replace.filename)
++				print_error("--atomic incompatible with "
++				   "command");
+ 			replace.filename = (char *)malloc(strlen(optarg) + 1);
+ 			strcpy(replace.filename, optarg);
+ 			// get the information from the file
+ 			get_table(&replace);
+                         if (replace.nentries) {
+-                                counterchanges = (unsigned short *)
++                                replace.counterchanges = (unsigned short *)
+                                    malloc(sizeof(unsigned short) * (replace.nentries + 1));
+ 				for (i = 0; i < replace.nentries; i++)
+-                                        counterchanges[i] = CNT_NORM;
+-                                counterchanges[i] = CNT_END;
++					replace.counterchanges[i] = CNT_NORM;
++					replace.counterchanges[i] = CNT_END;
+                         }
++			// we don't want the kernel giving us its counters, they would
++			// overwrite the counters extracted from the file
++			replace.num_counters = 0;
++			// make sure the table will be written to the kernel
+ 			free(replace.filename);
+ 			replace.filename = NULL;
+ 			break;
+@@ -2125,24 +2047,23 @@
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
+-			if ( !(table = find_table(replace.name)) )
+-				print_error("Bad table name");
+-			if (get_table(&replace)) {
+-				ebtables_insmod("ebtables", modprobe);
+-				if (get_table(&replace))
+-					print_error("can't initialize ebtables "
+-					"table %s", replace.name);
+-			}
++			if (replace.filename)
++				print_error("--atomic incompatible with "
++				   "command");
++			get_kernel_table(modprobe);
+ 			if (replace.nentries) {
+-				counterchanges = (unsigned short *)
++				replace.counterchanges = (unsigned short *)
+ 				   malloc(sizeof(unsigned short) * (replace.nentries + 1));
+ 				for (i = 0; i < replace.nentries; i++)
+-					counterchanges[i] = CNT_NORM;
+-				counterchanges[i] = CNT_END;
++					replace.counterchanges[i] = CNT_NORM;
++				replace.counterchanges[i] = CNT_END;
+ 			}
+ 			if (c == 11)
+ 				break;
+ 		case 9 : // atomic
++			if (c == 9 && (replace.flags & OPT_COMMAND))
++				print_error("--atomic has to come before"
++				" the command");
+ 			replace.filename = (char *)malloc(strlen(optarg) + 1);
+ 			strcpy(replace.filename, optarg);
+ 			break;
+@@ -2179,7 +2100,7 @@
+ check_extension:
+ 			if (replace.command != 'A' && replace.command != 'I' &&
+ 			   replace.command != 'D')
+-				print_error("extensions only for -A, -I and -D");
++				print_error("Extensions only for -A, -I and -D");
+ 		}
+ 	}
+ 
+@@ -2195,12 +2116,6 @@
+ 	   replace.flags & OPT_ZERO )
+ 		print_error("Command -Z only allowed together with command -L");
+ 
+-	if (replace.command == 'A' || replace.command == 'I' ||
+-	   replace.command == 'D') {
+-		if (replace.selected_hook == -1)
+-			print_error("Not enough information");
+-	}
+-
+ 	// do this after parsing everything, so we can print specific info
+ 	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ 		print_help();
+@@ -2283,7 +2198,7 @@
+ 
+ 	deliver_table(&replace);
+ 
+-	if (counterchanges)
+-		deliver_counters(&replace, counterchanges);
++	if (replace.counterchanges)
++		deliver_counters(&replace);
+ 	return 0;
+ }
+--- ebtables-v2.0pre10/communication.c	Mon Jul 15 22:35:14 2002
++++ ebtables-v2.0-rc1.001/communication.c	Fri Jul 26 13:33:02 2002
+@@ -1,5 +1,5 @@
+ /*
+- * communication.c, v2.0 April 2002
++ * communication.c, v2.0 July 2002
+  *
+  * Author: Bart De Schuymer
+  *
+@@ -18,9 +18,8 @@
+ #include <stdlib.h>
+ #include <sys/socket.h>
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/br_db.h> // the database
++#include <linux/br_db.h>
+ #include <netinet/in.h> // IPPROTO_IP
+-#include <asm/types.h>
+ #include "include/ebtables_u.h"
+ 
+ extern char* hooknames[NF_BR_NUMHOOKS];
+@@ -32,7 +31,8 @@
+ 	if (sockfd == -1) {
+ 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+ 		if (sockfd < 0)
+-			print_error("Problem getting a socket");
++			print_error("Problem getting a socket, "
++			   "do you have the right permissions?");
+ 	}
+ }
+ 
+@@ -52,7 +52,7 @@
+ 	if (!new)
+ 		print_memory();
+ 	new->valid_hooks = u_repl->valid_hooks;
+-	memcpy(new->name, u_repl->name, sizeof(new->name));
++	strcpy(new->name, u_repl->name);
+ 	new->nentries = u_repl->nentries;
+ 	new->num_counters = u_repl->num_counters;
+ 	new->counters = u_repl->counters;
+@@ -150,12 +150,10 @@
+ 			tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
+ 			tmp->invflags = e->invflags;
+ 			tmp->ethproto = e->ethproto;
+-			memcpy(tmp->in, e->in, sizeof(tmp->in));
+-			memcpy(tmp->out, e->out, sizeof(tmp->out));
+-			memcpy(tmp->logical_in, e->logical_in,
+-			   sizeof(tmp->logical_in));
+-			memcpy(tmp->logical_out, e->logical_out,
+-			   sizeof(tmp->logical_out));
++			strcpy(tmp->in, e->in);
++			strcpy(tmp->out, e->out);
++			strcpy(tmp->logical_in, e->logical_in);
++			strcpy(tmp->logical_out, e->logical_out);
+ 			memcpy(tmp->sourcemac, e->sourcemac,
+ 			   sizeof(tmp->sourcemac));
+ 			memcpy(tmp->sourcemsk, e->sourcemsk,
+@@ -190,7 +188,8 @@
+ 				   (struct ebt_standard_target *)p;
+ 				// translate the jump to a udc
+ 				if (st->verdict >= 0)
+-					st->verdict = chain_offsets[st->verdict + NF_BR_NUMHOOKS];
++					st->verdict = chain_offsets
++					   [st->verdict + NF_BR_NUMHOOKS];
+ 			}
+ 			p += e->t->target_size +
+ 			   sizeof(struct ebt_entry_target);
+@@ -257,23 +256,23 @@
+ 
+ 	// translate the struct ebt_u_replace to a struct ebt_replace
+ 	repl = translate_user2kernel(u_repl);
+-	// give the data to the kernel
+-	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	if (u_repl->filename != NULL) {
+ 		store_table_in_file(u_repl->filename, repl);
+ 		return;
+ 	}
++	// give the data to the kernel
++	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	get_sockfd();
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+ 		print_error("The kernel doesn't support a certain ebtables"
+ 		  " extension, consider recompiling your kernel or insmod"
+-		  " the extension");	
++		  " the extension");
+ }
+ 
+ static void store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+ {
+ 	int size = repl->nentries * sizeof(struct ebt_counter);
+-	int entries_size;
++	unsigned int entries_size;
+ 	struct ebt_replace hlp;
+ 	FILE *file;
+ 
+@@ -296,12 +295,13 @@
+ 
+ // gets executed after deliver_table
+ void
+-deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
++deliver_counters(struct ebt_u_replace *u_repl)
+ {
+ 	unsigned short *point;
+ 	struct ebt_counter *old, *new, *newcounters;
+ 	socklen_t optlen;
+ 	struct ebt_replace repl;
++	unsigned short *counterchanges = u_repl->counterchanges;
+ 
+ 	if (u_repl->nentries == 0)
+ 		return;
+@@ -323,16 +323,14 @@
+ 			old++;
+ 			// we've set a new counter
+ 			new++;
+-		} else
+-		if (*point == CNT_DEL) {
++		} else if (*point == CNT_DEL) {
+ 			// don't use this old counter
+ 			old++;
+ 		} else if (*point == CNT_ADD) {
+ 			// new counter, let it stay 0
+ 			new++;
+ 		} else {
+-			// zero it
+-			new->pcnt = 0;
++			// zero it (let it stay 0)
+ 			old++;
+ 			new++;
+ 		}
+@@ -355,7 +353,7 @@
+ 
+ 	get_sockfd();
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+-		print_bug("couldn't update kernel counters");
++		print_bug("Couldn't update kernel counters");
+ }
+ 
+ static int
+@@ -425,12 +423,10 @@
+ 		new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
+ 		new->invflags = e->invflags;
+ 		new->ethproto = e->ethproto;
+-		memcpy(new->in, e->in, sizeof(new->in));
+-		memcpy(new->out, e->out, sizeof(new->out));
+-		memcpy(new->logical_in, e->logical_in,
+-		   sizeof(new->logical_in));
+-		memcpy(new->logical_out, e->logical_out,
+-		   sizeof(new->logical_out));
++		strcpy(new->in, e->in);
++		strcpy(new->out, e->out);
++		strcpy(new->logical_in, e->logical_in);
++		strcpy(new->logical_out, e->logical_out);
+ 		memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
+ 		memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
+ 		memcpy(new->destmac, e->destmac, sizeof(new->destmac));
+@@ -500,9 +496,8 @@
+ 			while (i-- > 0)
+ 				cl = cl->next;
+ 			*u_e = &(cl->udc->entries);
+-		} else {
++		} else
+ 			*u_e = &(u_repl->hook_entry[*hook]->entries);
+-		}
+ 		return 0;
+ 	}
+ }
+@@ -568,7 +563,7 @@
+ 		print_error("Could not open file %s", filename);
+ 	// make sure table name is right if command isn't -L or --atomic-commit
+ 	if (command != 'L' && command != 8) {
+-		hlp = (char *)malloc(strlen(repl->name));
++		hlp = (char *)malloc(strlen(repl->name) + 1);
+ 		if (!hlp)
+ 			print_memory();
+ 		strcpy(hlp, repl->name);
+@@ -580,12 +575,11 @@
+ 		fclose(file);
+ 		print_error("File %s contains wrong table name or is corrupt",
+ 		   filename);
+-	} else
+-		if (!find_table(repl->name)) {
+-			fclose(file);
+-			print_error("File %s contains invalid table name",
+-			   filename);
+-		}
++		free(hlp);
++	} else if (!find_table(repl->name)) {
++		fclose(file);
++		print_error("File %s contains invalid table name", filename);
++	}
+ 
+ 	size = sizeof(struct ebt_replace) +
+ 	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+@@ -633,7 +627,7 @@
+ 	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+ 		return -1;
+ 
+-	if ( !(repl->entries = (char *) malloc(repl->entries_size)) )
++	if ( !(repl->entries = (char *)malloc(repl->entries_size)) )
+ 		print_memory();
+ 	if (repl->nentries) {
+ 		if (!(repl->counters = (struct ebt_counter *)
+@@ -657,7 +651,6 @@
+ 	return 0;
+ }
+ 
+-// talk with kernel to receive the kernel's table
+ int get_table(struct ebt_u_replace *u_repl)
+ {
+ 	int i, j, k, hook;
+@@ -667,12 +660,10 @@
+ 	strcpy(repl.name, u_repl->name);
+ 	if (u_repl->filename != NULL)
+ 		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
+-	else
+-		if (retrieve_from_kernel(&repl, u_repl->command) == -1)
+-			return -1;
++	else if (retrieve_from_kernel(&repl, u_repl->command) == -1)
++		return -1;
+ 
+ 	// translate the struct ebt_replace to a struct ebt_u_replace
+-	memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
+ 	u_repl->valid_hooks = repl.valid_hooks;
+ 	u_repl->nentries = repl.nentries;
+ 	u_repl->num_counters = repl.num_counters;
+--- ebtables-v2.0pre10/extensions/ebt_redirect.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/extensions/ebt_redirect.c	Thu Jul 25 19:28:52 2002
+@@ -12,7 +12,7 @@
+ #define REDIRECT_TARGET '1'
+ static struct option opts[] =
+ {
+-	{ "redirect-target"    , required_argument, 0, REDIRECT_TARGET },
++	{ "redirect-target", required_argument, 0, REDIRECT_TARGET },
+ 	{ 0 }
+ };
+ 
+@@ -20,7 +20,7 @@
+ {
+ 	printf(
+ 	"redirect option:\n"
+-	" --redirect-target target   : ACCEPT, DROP or CONTINUE\n");
++	" --redirect-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+ }
+ 
+ static void init(struct ebt_entry_target *target)
+@@ -62,6 +62,13 @@
+    const struct ebt_entry_target *target, const char *name,
+    unsigned int hook_mask, unsigned int time)
+ {
++	struct ebt_redirect_info *redirectinfo =
++	   (struct ebt_redirect_info *)target->data;
++
++	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) &&
++	   redirectinfo->target == EBT_RETURN)
++		print_error("--redirect-target RETURN not allowed on base chain");
++	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if ( ((hook_mask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
+ 	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+ 		print_error("Wrong chain for redirect");
+--- ebtables-v2.0pre10/extensions/ebtable_broute.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/extensions/ebtable_broute.c	Wed Jul 24 20:44:43 2002
+@@ -5,7 +5,7 @@
+ 
+ static void print_help(char **hn)
+ {
+-	printf("Supported chain for the nat table:\n");
++	printf("Supported chain for the broute table:\n");
+ 	printf("%s\n",hn[NF_BR_BROUTING]);
+ }
+ 
+--- ebtables-v2.0pre10/extensions/ebt_nat.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/extensions/ebt_nat.c	Thu Jul 25 16:31:01 2002
+@@ -37,7 +37,7 @@
+ 	printf(
+ 	"snat options:\n"
+ 	" --to-src address       : MAC address to map source to\n"
+-	" --snat-target target   : ACCEPT, DROP or CONTINUE\n");
++	" --snat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+ }
+ 
+ static void print_help_d()
+@@ -45,7 +45,7 @@
+ 	printf(
+ 	"dnat options:\n"
+ 	" --to-dst address       : MAC address to map destination to\n"
+-	" --dnat-target target   : ACCEPT, DROP or CONTINUE\n");
++	" --dnat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+ }
+ 
+ static void init_s(struct ebt_entry_target *target)
+@@ -81,7 +81,7 @@
+ 		check_option(flags, OPT_SNAT);
+ 		to_source_supplied = 1;
+ 		if (!(addr = ether_aton(optarg)))
+-			print_error("Problem with specified to-source mac");
++			print_error("Problem with specified --to-source mac");
+ 		memcpy(natinfo->mac, addr, ETH_ALEN);
+ 		break;
+ 	case NAT_S_TARGET:
+@@ -116,7 +116,7 @@
+ 		to_dest_supplied = 1;
+ 		if (!(addr = ether_aton(optarg)))
+ 			print_error("Problem with specified "
+-			            "to-destination mac");
++			            "--to-destination mac");
+ 		memcpy(natinfo->mac, addr, ETH_ALEN);
+ 		break;
+ 	case NAT_D_TARGET:
+@@ -139,6 +139,11 @@
+    const struct ebt_entry_target *target, const char *name,
+    unsigned int hook_mask, unsigned int time)
+ {
++	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
++
++	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && natinfo->target == EBT_RETURN)
++		print_error("--snat-target RETURN not allowed on base chain");
++	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if (!(hook_mask & (1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+ 		print_error("Wrong chain for snat");
+ 	if (time == 0 && to_source_supplied == 0)
+@@ -149,6 +154,11 @@
+    const struct ebt_entry_target *target, const char *name,
+    unsigned int hook_mask, unsigned int time)
+ {
++	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
++
++	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && natinfo->target == EBT_RETURN)
++		print_error("--dnat-target RETURN not allowed on base chain");
++	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+ 	if (((hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) ||
+ 	   strcmp(name, "nat")) &&
+ 	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+--- ebtables-v2.0pre10/extensions/ebt_vlan.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/extensions/ebt_vlan.c	Wed Jul 24 12:18:25 2002
+@@ -41,7 +41,7 @@
+ 
+ #define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_
+ #define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_
+-#define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "!" : ""
++#define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "! " : ""
+ 
+ #define VLAN_ID    0
+ #define VLAN_PRIO  1
+@@ -60,9 +60,9 @@
+ static void print_help ()
+ {
+ 	printf ("802.1Q VLAN extension options:\n"
+-		"--vlan-id [!]id        : VLAN-tagged frame identifier, 0,1-4094 (integer)\n"
+-		"--vlan-prio [!]prio    : Priority-tagged frame user_priority, 0-7 (integer)\n"
+-		"--vlan-encap [!]proto  : Encapsulated protocol (hexadecimal)\n");
++		"--vlan-id [!] id        : VLAN-tagged frame identifier, 0,1-4094 (integer)\n"
++		"--vlan-prio [!] prio    : Priority-tagged frame user_priority, 0-7 (integer)\n"
++		"--vlan-encap [!] proto  : Encapsulated protocol (hexadecimal)\n");
+ }
+ 
+ /*
+@@ -124,7 +124,7 @@
+ 		 * Check arg value presence
+ 		 */
+ 		if (optind > argc)
+-			print_error ("Missing VLAN ID argument value\n");
++			print_error ("Missing VLAN ID argument value");
+ 		/*
+ 		 * Convert argv to long int,
+ 		 * set *end to end of argv string, 
+@@ -136,7 +136,7 @@
+ 		 */
+ 		if (i > 4094 || *end != '\0')
+ 			print_error
+-			    ("Specified VLAN ID is out of range (0-4094)\n");
++			    ("Specified VLAN ID is out of range (0-4094)");
+ 		/*
+ 		 * Set up parameter value
+ 		 */
+@@ -153,7 +153,7 @@
+ 			vlaninfo->invflags |= EBT_VLAN_PRIO;
+ 		if (optind > argc)
+ 			print_error
+-			    ("Missing user_priority argument value\n");
++			    ("Missing user_priority argument value");
+ 		/*
+ 		 * Convert argv to long int,
+ 		 * set *end to end of argv string, 
+@@ -165,7 +165,7 @@
+ 		 */
+ 		if (i >= 8 || *end != '\0')
+ 			print_error
+-			    ("Specified user_priority is out of range (0-7)\n");
++			    ("Specified user_priority is out of range (0-7)");
+ 		/*
+ 		 * Set up parameter value 
+ 		 */
+@@ -182,7 +182,7 @@
+ 			vlaninfo->invflags |= EBT_VLAN_ENCAP;
+ 		if (optind > argc)
+ 			print_error
+-			    ("Missing encapsulated frame type argument value\n");
++			    ("Missing encapsulated frame type argument value");
+ 		/*
+ 		 * Parameter can be decimal, hexadecimal, or string.
+ 		 * Check arg val range (still raw area)
+@@ -190,12 +190,12 @@
+ 		(unsigned short) encap = strtol (argv[optind - 1], &end, 16);
+ 		if (*end == '\0' && (encap < ETH_ZLEN || encap > 0xFFFF))
+ 			print_error
+-			    ("Specified encapsulated frame type is out of range\n");
++			    ("Specified encapsulated frame type is out of range");
+ 		if (*end != '\0')
+ 			if (name_to_number (argv[optind - 1], &encap) == -1)
+ 				print_error
+ 				    ("Problem with the specified encapsulated"
+-				     "protocol\n");
++				     "protocol");
+ 		/*
+ 		 * Set up parameter value (network notation)
+ 		 */
+@@ -227,7 +227,7 @@
+ 	 */
+ 	if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
+ 		print_error
+-		    ("For use 802.1Q extension the protocol must be specified as 802_1Q\n");
++		    ("For use 802.1Q extension the protocol must be specified as 802_1Q");
+ 	/*
+ 	 * Check if specified vlan-id=0 (priority-tagged frame condition) 
+ 	 * when vlan-prio was specified.
+@@ -235,7 +235,7 @@
+ 	if (GET_BITMASK (EBT_VLAN_PRIO)) {
+ 		if (vlaninfo->id && GET_BITMASK (EBT_VLAN_ID))
+ 			print_error
+-			    ("For use user_priority the specified vlan-id must be 0\n");
++			    ("For use user_priority the specified vlan-id must be 0");
+ 	}
+ }
+ 
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0-rc1.001/extensions/ebt_mark.c	Thu Jul 25 16:51:14 2002
+@@ -0,0 +1,132 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <getopt.h>
++#include "../include/ebtables_u.h"
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++
++extern char *standard_targets[NUM_STANDARD_TARGETS];
++
++int mark_supplied;
++
++#define MARK_TARGET '1'
++#define MARK_SETMARK '2'
++static struct option opts[] =
++{
++	{ "mark-target"    , required_argument, 0, MARK_TARGET },
++	{ "set-mark"    , required_argument, 0, MARK_SETMARK },
++	{ 0 }
++};
++
++static void print_help()
++{
++	printf(
++	"mark target options:\n"
++	" --set-mark value   : Set nfmark value\n"
++	" --mark-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
++}
++
++static void init(struct ebt_entry_target *target)
++{
++	struct ebt_mark_t_info *markinfo =
++	   (struct ebt_mark_t_info *)target->data;
++
++	markinfo->target = EBT_ACCEPT;
++	markinfo->mark = 0;
++	mark_supplied = 0;
++	return;
++}
++
++#define OPT_MARK_TARGET  0x01
++#define OPT_MARK_SETMARK  0x02
++static int parse(int c, char **argv, int argc,
++   const struct ebt_u_entry *entry, unsigned int *flags,
++   struct ebt_entry_target **target)
++{
++	int i;
++	struct ebt_mark_t_info *markinfo =
++	   (struct ebt_mark_t_info *)(*target)->data;
++	char *end;
++
++	switch (c) {
++	case MARK_TARGET:
++		check_option(flags, OPT_MARK_TARGET);
++		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
++			if (!strcmp(optarg, standard_targets[i])) {
++				markinfo->target = -i - 1;
++				break;
++			}
++		if (i == NUM_STANDARD_TARGETS)
++			print_error("Illegal --mark-target target");
++		break;
++	case MARK_SETMARK:
++		check_option(flags, OPT_MARK_SETMARK);
++		markinfo->mark = strtoul(optarg, &end, 0);
++		if (*end != '\0' || end == optarg)
++			print_error("Bad MARK value '%s'", optarg);
++		mark_supplied = 1;
++                break;
++	 default:
++		return 0;
++	}
++	return 1;
++}
++
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target, const char *name,
++   unsigned int hook_mask, unsigned int time)
++{
++	struct ebt_mark_t_info *markinfo =
++	   (struct ebt_mark_t_info *)target->data;
++
++	if (time == 0 && mark_supplied == 0)
++		print_error("No mark value supplied");
++	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && markinfo->target == EBT_RETURN)
++		print_error("--mark-target RETURN not allowed on base chain");
++}
++
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target)
++{
++	struct ebt_mark_t_info *markinfo =
++	   (struct ebt_mark_t_info *)target->data;
++
++	printf("--set-mark 0x%lx", markinfo->mark);
++	if (markinfo->target == EBT_ACCEPT)
++		return;
++	printf(" --mark-target %s",
++	   standard_targets[-markinfo->target - 1]);
++}
++
++static int compare(const struct ebt_entry_target *t1,
++   const struct ebt_entry_target *t2)
++{
++	struct ebt_mark_t_info *markinfo1 =
++	   (struct ebt_mark_t_info *)t1->data;
++	struct ebt_mark_t_info *markinfo2 =
++	   (struct ebt_mark_t_info *)t2->data;
++
++	return markinfo1->target == markinfo2->target &&
++	   markinfo1->mark == markinfo2->mark;
++}
++
++static struct ebt_u_target mark_target =
++{
++	EBT_MARK_TARGET,
++	sizeof(struct ebt_mark_t_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts,
++};
++
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
++{
++	register_target(&mark_target);
++}
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0-rc1.001/extensions/ebt_mark_m.c	Sun Jul 21 17:12:04 2002
+@@ -0,0 +1,123 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <sys/socket.h>
++#include <string.h>
++#include <getopt.h>
++#include "../include/ebtables_u.h"
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++
++#define MARK '1'
++
++static struct option opts[] =
++{
++	{ "mark"     , required_argument, 0, MARK },
++	{ 0 }
++};
++
++static void print_help()
++{
++	printf(
++"mark option:\n"
++"--mark    [!] [value][/mask]: Match nfmask value (see man page)\n");
++}
++
++static void init(struct ebt_entry_match *match)
++{
++	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)match->data;
++
++	markinfo->mark = 0;
++	markinfo->mask = 0;
++	markinfo->invert = 0;
++	markinfo->bitmask = 0;
++}
++
++#define OPT_MARK 0x01
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_match **match)
++{
++	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)
++	   (*match)->data;
++	char *end;
++
++	switch (c) {
++	case MARK:
++		check_option(flags, MARK);
++		if (check_inverse(optarg))
++			markinfo->invert = 1;
++		if (optind > argc)
++			print_error("No mark specified");
++		markinfo->mark = strtoul(argv[optind - 1], &end, 0);
++		markinfo->bitmask = EBT_MARK_AND;
++		if (*end == '/') {
++			if (end == argv[optind - 1])
++				markinfo->bitmask = EBT_MARK_OR;
++			markinfo->mask = strtoul(end+1, &end, 0);
++		} else
++			markinfo->mask = 0xffffffff;
++		if ( *end != '\0' || end == argv[optind - 1])
++			print_error("Bad mark value '%s'", argv[optind - 1]);
++		break;
++	default:
++		return 0;
++	}
++	return 1;
++}
++
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match, const char *name,
++   unsigned int hook_mask, unsigned int time)
++{
++}
++
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match)
++{
++	struct ebt_mark_m_info *markinfo =
++	   (struct ebt_mark_m_info *)match->data;
++
++	printf("--mark ");
++	if (markinfo->invert)
++		printf("! ");
++	if (markinfo->bitmask == EBT_MARK_OR)
++		printf("/0x%lx ", markinfo->mask);
++	else if(markinfo->mask != 0xffffffff)
++		printf("0x%lx/0x%lx ", markinfo->mark, markinfo->mask);
++	else
++		printf("0x%lx ", markinfo->mark);
++}
++
++static int compare(const struct ebt_entry_match *m1,
++   const struct ebt_entry_match *m2)
++{
++	struct ebt_mark_m_info *markinfo1 = (struct ebt_mark_m_info *)m1->data;
++	struct ebt_mark_m_info *markinfo2 = (struct ebt_mark_m_info *)m2->data;
++
++	if (markinfo1->invert != markinfo2->invert)
++		return 0;
++	if (markinfo1->mark != markinfo2->mark)
++		return 0;
++	if (markinfo1->mask != markinfo2->mask)
++		return 0;
++	if (markinfo1->bitmask != markinfo2->bitmask)
++		return 0;
++	return 1;
++}
++
++static struct ebt_u_match mark_match =
++{
++	EBT_MARK_MATCH,
++	sizeof(struct ebt_mark_m_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts,
++};
++
++static void _init(void) __attribute((constructor));
++static void _init(void)
++{
++	register_match(&mark_match);
++}
+--- ebtables-v2.0pre10/extensions/Makefile	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/extensions/Makefile	Sat Jul 20 15:34:51 2002
+@@ -1,6 +1,6 @@
+ #! /usr/bin/make
+ 
+-EXT_FUNC+=nat arp ip standard log redirect vlan
++EXT_FUNC+=nat arp ip standard log redirect vlan mark_m mark
+ EXT_TABLES+=filter nat broute
+ EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+ EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+--- ebtables-v2.0pre10/ChangeLog	Sun Jul 14 21:30:18 2002
++++ ebtables-v2.0-rc1.001/ChangeLog	Tue Jul 30 23:05:30 2002
+@@ -1,3 +1,16 @@
++20020730
++	* other things done before 2.0-rc1 that I can think of,
++	  including kernel:
++	* cache align counters for better smp performance
++	* simplify snat code
++	* check for --xxxx-target RETURN on base chain
++	* cleanup code
++	* minor bugfixes
++20020724
++	* code cleanup
++	* bugfix for --atomic-commit
++20020720
++	* added mark target+match
+ 20020714
+ 	* added --atomic options
+ 20020710
+--- ebtables-v2.0pre10/ebtables.8	Mon Jul 15 21:45:55 2002
++++ ebtables-v2.0-rc1.001/ebtables.8	Thu Jul 25 17:01:17 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "26 June 2002"
++.TH EBTABLES 8  "23 July 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -21,7 +21,7 @@
+ .\"     
+ .\"
+ .SH NAME
+-ebtables (v.2.0) \- ethernet bridge packet table administration
++ebtables (v.2.0) \- Ethernet bridge packet table administration
+ .SH SYNOPSIS
+ .BR "ebtables -[ADI] " "chain rule-specification " [ options ]
+ .br
+@@ -335,7 +335,7 @@
+ .B ebtables
+ will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
+ .TP
+-.BR "-b --db [" "y/n" "]"
++.BR "-b --db " [ "y/n" ]
+ Enable (y) or disable (n) the database.
+ .TP
+ .BR "-j, --jump " "\fItarget\fP"
+@@ -346,13 +346,13 @@
+ or a target extension, see
+ .BR "TARGET EXTENSIONS" .
+ .TP
+-.BR "--atomic " file
++.B --atomic file
+ Let the command operate on the specified file. The data of the table to
+ operate on will be extracted from the file and the result of the operation
+ will be saved back into the file. If specified, this option should come
+ before the command specification.
+ .TP
+-.BR "-M, --modprobe " "program"
++.B -M, --modprobe program
+ When talking to the kernel, use this program to try to automatically load
+ missing kernel modules.
+ .SH MATCH EXTENSIONS
+@@ -390,7 +390,8 @@
+ .BR ARP " or " RARP .
+ .TP
+ .BR "--arp-opcode " "[!] \fIopcode\fP"
+-The (r)arp opcode (decimal or a string, for more details see ebtables -h arp).
++The (r)arp opcode (decimal or a string, for more details see
++.BR "ebtables -h arp" ).
+ .TP
+ .BR "--arp-htype " "[!] \fIhardware type\fP"
+ The hardware type, this can be a decimal or the string "Ethernet". This
+@@ -419,13 +420,24 @@
+ Required VID to be 0 (null VID) or not specified vlan-id parameter (in this case VID deliberately be set to 0).
+ .TP
+ .BR "--vlan-encap " "[!] \fItype\fP"
+-The encapsulated ethernet frame type/length, this can be a hexadecimal number from 0x0000 to 0xFFFF.
++The encapsulated Ethernet frame type/length, this can be a hexadecimal
++number from 0x0000 to 0xFFFF.
+ Usually it's 0x0800 (IPv4). See also 
+ .B /etc/ethertypes 
+ file.
++.SS mark_m
++.TP
++.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
++Matches frames with the given unsigned mark value. If a mark value and 
++mask is specified, the logical AND of the mark value of the frame and
++the user specified mask is taken before comparing with the user specified
++mark value. If only a mask is specified (start with '/') the logical AND
++of the mark value of the frame and the user specified mark is taken and
++the result is compared with zero.
++
+ .SH WATCHER EXTENSION(S)
+-Watchers are things that only look at frames passing by. These watchers only see the
+-frame if the frame passes all the matches of the rule.
++Watchers are things that only look at frames passing by. These watchers only
++see the frame if the frame passes all the matches of the rule.
+ .SS log
+ The fact that the log module is a watcher lets us log stuff while giving a target
+ by choice. Note that the log module therefore is not a target.
+@@ -478,7 +490,7 @@
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP doesn't
+ make sense, but you could do that too. RETURN is also allowed. Note
+-that using RETURN in a base chain will result in the CONTINUE behaviour.
++that using RETURN in a base chain is not allowed.
+ .TP
+ .B dnat
+ The
+@@ -504,7 +516,7 @@
+ multiple target extensions on the same frame. Making it DROP only makes
+ sense in the BROUTING chain but using the redirect target is more logical
+ there. RETURN is also allowed. Note
+-that using RETURN in a base chain will result in the CONTINUE behaviour.
++that using RETURN in a base chain is not allowed.
+ .TP
+ .B redirect
+ The
+@@ -523,7 +535,28 @@
+ The default target is ACCEPT. Making it CONTINUE could let you use 
+ multiple target extensions on the same frame. Making it DROP in the
+ BROUTING chain will let the frames be routed. RETURN is also allowed. Note
+-that using RETURN in a base chain will result in the CONTINUE behaviour.
++that using RETURN in a base chain is not allowed.
++.TP
++.B mark
++The mark target can be used in every chain of every table. It is possible
++to use the marking of a frame/packet in both ebtables and iptables, 
++if the br-nf code is compiled into the kernel. Both put the marking at the
++same place. So, you can consider this fact as a feature, or as something to
++watch out for.
++.br
++.BR "--mark-target " "\fItarget\fP"
++.br
++Specifies the standard target. After marking the frame, the rule
++still has to give a standard target so
++.B ebtables
++knows what to do.
++The default target is ACCEPT. Making it CONTINUE can let you do other
++things with the frame in other rules of the chain.
++.br
++.BR "--set-mark " "\fIvalue\fP"
++.br
++Mark the frame with the specified unsigned value.
++.br
+ .SH FILES
+ .I /etc/ethertypes
+ .SH BUGS
+--- ebtables-v2.0pre10/ethertypes	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc1.001/ethertypes	Fri Jul 19 20:44:25 2002
+@@ -3,15 +3,14 @@
+ # all protocol numbers are in hexadecimal form
+ # maximum namesize = 20 characters
+ # always put tabs or spaces between the name and the protocol number
+-# don't use more than 4 digits for the protocol number
++# anything on a line after the protocol number is ignored
+ # programs using this file should not be case sensitive
+-# that's all :-))
+-IPV4 	0800	put your comments behind, on the same line, after a tab
+-X25	0805    or whitespace
++IPv4 	0800
++X25	0805
+ ARP	0806
+ 802_1Q	8100	802.1Q Virtual LAN tagged frame
+ IPX	8137
+-IPV6	86DD
++IPv6	86DD
+ NetBEUI	8191
+ BPQ	08FF	G8BPQ AX.25 Ethernet Packet
+ DEC	6000	DEC Assigned proto
+--- ebtables-v2.0pre10/include/ebtables_u.h	Sat Jul 13 21:13:46 2002
++++ ebtables-v2.0-rc1.001/include/ebtables_u.h	Fri Jul 26 14:03:04 2002
+@@ -29,7 +29,7 @@
+ struct ebt_u_entries
+ {
+ 	int policy;
+-	__u32 nentries;
++	unsigned int nentries;
+ 	// counter offset for this chain
+ 	unsigned int counter_offset;
+ 	// used for udc
+@@ -42,7 +42,7 @@
+ {
+ 	struct ebt_u_entries *udc;
+ 	struct ebt_u_chain_list *next;
+-	// this is only used internally, in communications.c
++	// this is only used internally, in communication.c
+ 	char *kernel_start;
+ };
+ 
+@@ -68,6 +68,8 @@
+ 	int selected_hook;
+ 	// used for the atomic option
+ 	char *filename;
++	// tells what happened to the old rules
++	unsigned short *counterchanges;
+ };
+ 
+ struct ebt_u_table
+@@ -92,17 +94,17 @@
+ 
+ struct ebt_u_entry
+ {
+-	__u32 bitmask;
+-	__u32 invflags;
++	unsigned int bitmask;
++	unsigned int invflags;
+ 	__u16 ethproto;
+-	__u8 in[IFNAMSIZ];
+-	__u8 logical_in[IFNAMSIZ];
+-	__u8 out[IFNAMSIZ];
+-	__u8 logical_out[IFNAMSIZ];
+-	__u8 sourcemac[ETH_ALEN];
+-	__u8 sourcemsk[ETH_ALEN];
+-	__u8 destmac[ETH_ALEN];
+-	__u8 destmsk[ETH_ALEN];
++	char in[IFNAMSIZ];
++	char logical_in[IFNAMSIZ];
++	char out[IFNAMSIZ];
++	char logical_out[IFNAMSIZ];
++	unsigned char sourcemac[ETH_ALEN];
++	unsigned char sourcemsk[ETH_ALEN];
++	unsigned char destmac[ETH_ALEN];
++	unsigned char destmsk[ETH_ALEN];
+ 	struct ebt_u_match_list *m_list;
+ 	struct ebt_u_watcher_list *w_list;
+ 	struct ebt_entry_target *t;
+@@ -194,8 +196,7 @@
+ struct ebt_u_match *find_match(const char *name);
+ struct ebt_u_watcher *find_watcher(const char *name);
+ struct ebt_u_table *find_table(char *name);
+-void deliver_counters(struct ebt_u_replace *repl,
+-   unsigned short * counterchanges);
++void deliver_counters(struct ebt_u_replace *repl);
+ void deliver_table(struct ebt_u_replace *repl);
+ void get_dbinfo(struct brdb_dbinfo *nr);
+ void get_db(int len, struct brdb_dbentry *db);
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0-rc2.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0-rc2.001.diff
new file mode 100644
index 0000000..d39d9ad
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0-rc2.001.diff
@@ -0,0 +1,1730 @@
+--- ebtables-v2.0-rc1/Makefile	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/Makefile	Sun Aug 11 17:28:10 2002
+@@ -2,7 +2,8 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0-rc1 (July 2002)"
++PROGVERSION:="2.0-rc2"
++PROGDATE:="August 2002"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -22,8 +23,6 @@
+ 	mkdir -p /usr/include/linux/netfilter_bridge
+ 	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge/* \
+ 		/usr/include/linux/netfilter_bridge/
+-	cp -f $(KERNEL_DIR)/include/linux/br_db.h \
+-		/usr/include/linux/br_db.h
+ 	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \
+ 		/usr/include/linux/netfilter_bridge.h
+ 	cp -f $(KERNEL_DIR)/include/linux/if_ether.h \
+@@ -35,11 +34,11 @@
+ 	ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux
+ 
+ communication.o: communication.c include/ebtables_u.h
+-	$(CC) $(CFLAGS) -c -o $@ $<
++	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" -c -o $@ $<
+ 
+ ebtables.o: ebtables.c include/ebtables_u.h
+ 	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \
+-	-DPROGNAME=\"$(PROGNAME)\" -c -o $@ $<
++	-DPROGNAME=\"$(PROGNAME)\" -DPROGDATE=\"$(PROGDATE)\" -c -o $@ $<
+ 
+ ebtables: ebtables.o communication.o $(EXT_OBJS)
+ 	$(CC) $(CFLAGS) -o $@ $^
+--- ebtables-v2.0-rc1/ebtables.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/ebtables.c	Sat Aug 24 20:32:04 2002
+@@ -23,20 +23,28 @@
+ 
+ #include <getopt.h>
+ #include <string.h>
+-#include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+-#include <sys/types.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/br_db.h> // the database
+-#include <netinet/in.h>
++#include <stdarg.h>
+ #include <netinet/ether.h>
+ #include "include/ebtables_u.h"
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+ 
++// Don't use this function, use print_bug()
++void __print_bug(char *file, int line, char *format, ...)
++{
++	va_list l;
++
++	va_start(l, format);
++	printf(PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line);
++	vprintf(format, l);
++	printf("\n");
++	va_end(l);
++	exit (-1);
++}
++
+ // here are the number-name correspondences kept for the Ethernet
+ // frame type field
+ #define PROTOCOLFILE "/etc/ethertypes"
+@@ -45,11 +53,6 @@
+ #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+ #endif
+ 
+-#define DATABASEHOOKNR -2
+-#define DATABASEHOOKNAME "DB"
+-
+-static char *prog_name = PROGNAME;
+-static char *prog_version = PROGVERSION;
+ char *hooknames[NF_BR_NUMHOOKS] =
+ {
+ 	[NF_BR_PRE_ROUTING]"PREROUTING",
+@@ -193,7 +196,7 @@
+ 	// on CONTINUE
+ 	e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+ 	if (!e->t)
+-		print_bug("Couldn't load standard target\n");
++		print_bug("Couldn't load standard target");
+ }
+ 
+ // this doesn't free e, becoz the calling function might need e->next
+@@ -432,6 +435,7 @@
+ 
+ // translate a hexadecimal number to a protocol name, parsing /etc/ethertypes
+ // returns 0 on success
++// this demands the name buffer to be of size at least 21
+ int number_to_name(unsigned short proto, char *name)
+ {
+ 	FILE *ifp;
+@@ -476,9 +480,9 @@
+ 		printf("ebtables -t %s -P %s %s\n", replace.name,
+ 		   entries->name, standard_targets[-entries->policy - 1]);
+ 	} else if (!(replace.flags & LIST_X)) {
+-		printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
++		printf("\nBridge chain: %s, entries: %d, policy: %s\n",
++		   entries->name, entries->nentries,
+ 		   standard_targets[-entries->policy - 1]);
+-		printf("nr. of entries: %d \n", entries->nentries);
+ 	}
+ 
+ 	i = entries->nentries;
+@@ -759,10 +763,6 @@
+ 	int i;
+ 	struct ebt_u_chain_list *cl = replace.udc;
+ 
+-	// database is special case (not really a chain)
+-	if (!strcmp(arg, DATABASEHOOKNAME))
+-		return DATABASEHOOKNR;
+-
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if (!(replace.valid_hooks & (1 << i)))
+ 			continue;
+@@ -784,27 +784,25 @@
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+ 
+-	printf(
+-"%s v%s\n"
++	printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n"
+ "Usage:\n"
+ "ebtables -[ADI] chain rule-specification [options]\n"
+ "ebtables -P chain target\n"
+ "ebtables -[LFZ] [chain]\n"
+ "ebtables -[b] [y,n]\n"
+ "Commands:\n"
+-"--append -A chain             : Append to chain\n"
+-"--delete -D chain             : Delete matching rule from chain\n"
+-"--delete -D chain rulenum     : Delete rule at position rulenum from chain\n"
++"--append -A chain             : append to chain\n"
++"--delete -D chain             : delete matching rule from chain\n"
++"--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
+ "--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+-"--list   -L [chain]           : List the rules in a chain or in all chains\n"
+-"--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
+-"--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
+-"--init-table                  : Replace the kernel table with the initial table\n"
+-"--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
+-"--policy -P chain target      : Change policy on chain to target\n"
+-"--new-chain -N chain          : Create a user defined chain\n"
+-"--rename-chain -E old new     : Rename a chain\n"
+-"--delete-chain -X chain       : Delete a user defined chain\n"
++"--list   -L [chain]           : list the rules in a chain or in all chains\n"
++"--flush  -F [chain]           : delete all rules in chain or in all chains\n"
++"--init-table                  : replace the kernel table with the initial table\n"
++"--zero   -Z [chain]           : put counters on zero in chain or in all chains\n"
++"--policy -P chain target      : change policy on chain to target\n"
++"--new-chain -N chain          : create a user defined chain\n"
++"--rename-chain -E old new     : rename a chain\n"
++"--delete-chain -X chain       : delete a user defined chain\n"
+ "--atomic-commit file          : update the kernel w/ the table contained in file\n"
+ "--atomic-init file            : put the initial kernel table into file\n"
+ "--atomic-save file            : put the current kernel table into file\n"
+@@ -819,9 +817,7 @@
+ "--logical-out [!] name        : logical bridge output interface name\n"
+ "--modprobe -M program         : try to insert modules using this program\n"
+ "--version -V                  : print package version\n"
+-"\n" ,
+-	prog_name,
+-	prog_version);
++"\n");
+ 
+ 	m_l = new_entry->m_list;
+ 	while (m_l) {
+@@ -1311,66 +1307,6 @@
+ 	}
+ }
+ 
+-// list the database (optionally compiled into the kernel)
+-static void list_db()
+-{
+-	struct brdb_dbinfo nr;
+-	struct brdb_dbentry *db;
+-	char name[21];
+-	int i;
+-
+-	get_dbinfo(&nr);
+-
+-	// 0 : database disabled (-db n)
+-	if (!(nr.nentries))
+-		print_error("Database not present"
+-		            " (disabled), try ebtables --db y");
+-	nr.nentries--;
+-	if (!nr.nentries) print_error("Database empty");
+-	if ( !(db = (struct brdb_dbentry *)
+-	   malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
+-		print_memory();
+-
+-	get_db(nr.nentries, db);
+-	printf("number of entries: %d\n", nr.nentries);
+-	for (i = 0; i < nr.nentries; i++) {
+-		printf(
+-		"%d:\n"
+-		"hook    : %s\n"
+-		"in-if   : %s\n"
+-		"out-if  : %s\n"
+-		"protocol: ", i + 1, hooknames[db->hook], db->in, db->out);
+-		if (db->ethproto == IDENTIFY802_3)
+-			printf("802.2/802.3 STYLE LENGTH FIELD\n");
+-		else {
+-			if (number_to_name(ntohs(db->ethproto), name))
+-				printf("%x\n",ntohs(db->ethproto));
+-			else
+-				printf("%s\n", name);
+-		}
+-		db++;
+-	}
+-	exit(0);
+-}
+-
+-// handle db [dis,en]abling
+-static void allowdb(char yorn)
+-{
+-	__u16 decision;
+-
+-	if (yorn != 'y' && yorn != 'n')
+-		print_error("Option [y] or [n] needed");
+-
+-	if (yorn == 'y')
+-		decision = BRDB_DB;
+-	else
+-		decision = BRDB_NODB;
+-
+-	deliver_allowdb(&decision);
+-
+-	exit(0);
+-}
+-
+ //  0 == success
+ //  1 == success, but for the special 'protocol' LENGTH
+ // -1 == failure
+@@ -1526,8 +1462,14 @@
+ 			print_error("The kernel doesn't support the ebtables "
+ 			"%s table", replace.name);
+ 	}
++	// when listing a table contained in a file, we don't expect the user
++	// to know what the table's name is
++	if ( !(table = find_table(replace.name)) )
++		print_error("Bad table name");
+ }
+ 
++#define print_if_l_error print_error("Interface name length must be less " \
++   "than %d", IFNAMSIZ)
+ #define OPT_COMMAND    0x01
+ #define OPT_TABLE      0x02
+ #define OPT_IN         0x04
+@@ -1542,7 +1484,7 @@
+ // the main thing
+ int main(int argc, char *argv[])
+ {
+-	char *buffer, allowbc = 'n';
++	char *buffer;
+ 	int c, i;
+ 	// this special one for the -Z option (we can have -Z <this> -L <that>)
+ 	int zerochain = -1;
+@@ -1576,7 +1518,7 @@
+ 
+ 	// getopt saves the day
+ 	while ((c = getopt_long(argc, argv,
+-	   "-A:D:I:N:E:X:L::Z::F::P:Vhi:o:j:p:b:s:d:t:M:", ebt_options, NULL)) != -1) {
++	   "-A:D:I:N:E:X:L::Z::F::P:Vhi:o:j:p:s:d:t:M:", ebt_options, NULL)) != -1) {
+ 		switch (c) {
+ 
+ 		case 'A': // add a rule
+@@ -1738,7 +1680,7 @@
+ 			replace.command = 'V';
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+-			printf("%s, %s\n", prog_name, prog_version);
++			printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
+ 			exit(0);
+ 
+ 		case 'M': // modprobe
+@@ -1812,7 +1754,7 @@
+ 					print_error("No in-interface "
+ 					            "specified");
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interface length");
++					print_if_l_error;
+ 				strcpy(new_entry->in, argv[optind - 1]);
+ 				break;
+ 			}
+@@ -1830,7 +1772,7 @@
+ 					print_error("No logical in-interface "
+ 					            "specified");
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interface length");
++					print_if_l_error;
+ 				strcpy(new_entry->logical_in, argv[optind - 1]);
+ 				break;
+ 			}
+@@ -1848,8 +1790,7 @@
+ 					            "specified");
+ 
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interface "
+-					            "length");
++					print_if_l_error;
+ 				strcpy(new_entry->out, argv[optind - 1]);
+ 				break;
+ 			}
+@@ -1867,8 +1808,7 @@
+ 					            "specified");
+ 
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interface "
+-					            "length");
++					print_if_l_error;
+ 				strcpy(new_entry->logical_out,
+ 				   argv[optind - 1]);
+ 				break;
+@@ -1972,18 +1912,8 @@
+ 				            " or equal to 0x0600");
+ 			break;
+ 
+-		case 'b': // allow database?
+-			if (replace.flags & OPT_COMMAND)
+-				print_error("Multiple commands not allowed");
+-			replace.command = c;
+-			allowbc = *optarg;
+-			break;
+-
+ 		case 4  : // Lc
+ 			check_option(&replace.flags, LIST_C);
+-			if (replace.selected_hook == DATABASEHOOKNR)
+-				print_error("--Lc not valid for listing"
+-				   " the database");
+ 			if (replace.command != 'L')
+ 				print_error("Use --Lc with -L");
+ 			if (replace.flags & LIST_X)
+@@ -1992,9 +1922,6 @@
+ 			break;
+ 		case 5  : // Ln
+ 			check_option(&replace.flags, LIST_N);
+-			if (replace.selected_hook == DATABASEHOOKNR)
+-				print_error("--Ln not valid for listing"
+-				   " the database");
+ 			if (replace.command != 'L')
+ 				print_error("Use --Ln with -L");
+ 			if (replace.flags & LIST_X)
+@@ -2003,9 +1930,6 @@
+ 			break;
+ 		case 6  : // Lx
+ 			check_option(&replace.flags, LIST_X);
+-			if (replace.selected_hook == DATABASEHOOKNR)
+-				print_error("--Lx not valid for listing"
+-				   " the database");
+ 			if (replace.command != 'L')
+ 				print_error("Use --Lx with -L");
+ 			if (replace.flags & LIST_C)
+@@ -2039,6 +1963,7 @@
+ 			// make sure the table will be written to the kernel
+ 			free(replace.filename);
+ 			replace.filename = NULL;
++			ebtables_insmod("ebtables", modprobe);
+ 			break;
+ 		case 7 : // atomic-init
+ 		case 10: // atomic-save
+@@ -2106,11 +2031,6 @@
+ 
+ 	if ( !table && !(table = find_table(replace.name)) )
+ 		print_error("Bad table name");
+-	// database stuff before ebtables stuff
+-	if (replace.command == 'b')
+-		allowdb(allowbc);
+-	if (replace.command == 'L' && replace.selected_hook == DATABASEHOOKNR)
+-		list_db();
+ 
+ 	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
+ 	   replace.flags & OPT_ZERO )
+@@ -2148,9 +2068,13 @@
+ 	// the kernel does not have to do this ofcourse
+ 	new_entry->ethproto = htons(new_entry->ethproto);
+ 
+-	if (replace.command == 'P')
++	if (replace.command == 'P') {
++		if (replace.selected_hook < NF_BR_NUMHOOKS &&
++		   policy == EBT_RETURN)
++			print_error("Policy RETURN only allowed for user "
++			            "defined chains");
+ 		change_policy(policy);
+-	else if (replace.command == 'L') {
++	} else if (replace.command == 'L') {
+ 		list_rules();
+ 		if (replace.flags & OPT_ZERO)
+ 			zero_counters(zerochain);
+--- ebtables-v2.0-rc1/communication.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/communication.c	Sat Aug 24 20:33:51 2002
+@@ -17,9 +17,6 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/br_db.h>
+-#include <netinet/in.h> // IPPROTO_IP
+ #include "include/ebtables_u.h"
+ 
+ extern char* hooknames[NF_BR_NUMHOOKS];
+@@ -32,7 +29,8 @@
+ 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+ 		if (sockfd < 0)
+ 			print_error("Problem getting a socket, "
+-			   "do you have the right permissions?");
++			   "you probably don't have the right "
++			   "permissions");
+ 	}
+ }
+ 
+@@ -556,7 +554,7 @@
+    char command)
+ {
+ 	FILE *file;
+-	char *hlp;
++	char *hlp = NULL;
+ 	int size;
+ 
+ 	if (!(file = fopen(filename, "r+b")))
+@@ -658,9 +656,11 @@
+ 	struct ebt_u_entry **u_e;
+ 
+ 	strcpy(repl.name, u_repl->name);
+-	if (u_repl->filename != NULL)
++	if (u_repl->filename != NULL) {
+ 		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
+-	else if (retrieve_from_kernel(&repl, u_repl->command) == -1)
++		// -L with a wrong table name should be dealt with silently
++		strcpy(u_repl->name, repl.name);
++	} else if (retrieve_from_kernel(&repl, u_repl->command) == -1)
+ 		return -1;
+ 
+ 	// translate the struct ebt_replace to a struct ebt_u_replace
+@@ -682,38 +682,4 @@
+ 	if (k != u_repl->nentries)
+ 		print_bug("Wrong total nentries");
+ 	return 0;
+-}
+-
+-void get_dbinfo(struct brdb_dbinfo *nr)
+-{
+-	socklen_t optlen = sizeof(struct brdb_dbinfo);
+-
+-	get_sockfd();
+-
+-	if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
+-		print_error("Sorry, br_db code probably not in kernel, "
+-		            "try insmod br_db");
+-}
+-
+-void get_db(int len, struct brdb_dbentry *db)
+-{
+-	socklen_t optlen = len;
+-
+-	get_sockfd();
+-
+-	if ( getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DB, db, &optlen) ) {
+-		print_bug("hmm, what is wrong??? bug#2");
+-	}
+-}
+-
+-void deliver_allowdb(__u16 *decision)
+-{
+-	socklen_t optlen = sizeof(__u16);
+-
+-	get_sockfd();
+-
+-	if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB,
+-	   decision, optlen))
+-		print_error("Sorry, br_db code probably not in kernel, "
+-		            "try insmod br_db");
+ }
+--- ebtables-v2.0-rc1/extensions/ebt_redirect.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebt_redirect.c	Sat Aug 24 15:31:38 2002
+@@ -1,14 +1,10 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_redirect.h>
+ 
+-extern char *standard_targets[NUM_STANDARD_TARGETS];
+-
+ #define REDIRECT_TARGET '1'
+ static struct option opts[] =
+ {
+@@ -37,19 +33,13 @@
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
+-	int i;
+ 	struct ebt_redirect_info *redirectinfo =
+ 	   (struct ebt_redirect_info *)(*target)->data;
+ 
+ 	switch (c) {
+ 	case REDIRECT_TARGET:
+ 		check_option(flags, OPT_REDIRECT_TARGET);
+-		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+-			if (!strcmp(optarg, standard_targets[i])) {
+-				redirectinfo->target = -i - 1;
+-				break;
+-			}
+-		if (i == NUM_STANDARD_TARGETS)
++		if (FILL_TARGET(optarg, redirectinfo->target))
+ 			print_error("Illegal --redirect-target target");
+ 		break;
+ 	default:
+@@ -60,17 +50,17 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ 	struct ebt_redirect_info *redirectinfo =
+ 	   (struct ebt_redirect_info *)target->data;
+ 
+-	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) &&
+-	   redirectinfo->target == EBT_RETURN)
+-		print_error("--redirect-target RETURN not allowed on base chain");
+-	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+-	if ( ((hook_mask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
+-	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
++	if (BASE_CHAIN && redirectinfo->target == EBT_RETURN)
++		print_error("--redirect-target RETURN not allowed on "
++		            "base chain");
++	CLEAR_BASE_CHAIN_BIT;
++	if ( ((hookmask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
++	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+ 		print_error("Wrong chain for redirect");
+ }
+ 
+@@ -82,8 +72,7 @@
+ 
+ 	if (redirectinfo->target == EBT_ACCEPT)
+ 		return;
+-	printf(" --redirect-target %s",
+-	   standard_targets[-redirectinfo->target - 1]);
++	printf(" --redirect-target %s", TARGET_NAME(redirectinfo->target));
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+@@ -107,7 +96,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init(void) __attribute__ ((constructor));
+--- ebtables-v2.0-rc1/extensions/ebtable_broute.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebtable_broute.c	Tue Aug 13 12:59:14 2002
+@@ -1,5 +1,4 @@
+ #include <stdio.h>
+-#include <sys/socket.h>
+ #include "../include/ebtables_u.h"
+ 
+ 
+--- ebtables-v2.0-rc1/extensions/ebt_nat.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebt_nat.c	Sat Aug 24 15:31:03 2002
+@@ -1,16 +1,12 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <netinet/ether.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_nat.h>
+ 
+-extern char *standard_targets[NUM_STANDARD_TARGETS];
+-
+-int to_source_supplied, to_dest_supplied;
++static int to_source_supplied, to_dest_supplied;
+ 
+ #define NAT_S '1'
+ #define NAT_D '1'
+@@ -20,7 +16,7 @@
+ {
+ 	{ "to-source"     , required_argument, 0, NAT_S },
+ 	{ "to-src"        , required_argument, 0, NAT_S },
+-	{ "snat-target"    , required_argument, 0, NAT_S_TARGET },
++	{ "snat-target"   , required_argument, 0, NAT_S_TARGET },
+ 	{ 0 }
+ };
+ 
+@@ -28,7 +24,7 @@
+ {
+ 	{ "to-destination", required_argument, 0, NAT_D },
+ 	{ "to-dst"        , required_argument, 0, NAT_D },
+-	{ "dnat-target"    , required_argument, 0, NAT_D_TARGET },
++	{ "dnat-target"   , required_argument, 0, NAT_D_TARGET },
+ 	{ 0 }
+ };
+ 
+@@ -72,7 +68,6 @@
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
+-	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 	struct ether_addr *addr;
+ 
+@@ -86,12 +81,7 @@
+ 		break;
+ 	case NAT_S_TARGET:
+ 		check_option(flags, OPT_SNAT_TARGET);
+-		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+-			if (!strcmp(optarg, standard_targets[i])) {
+-				natinfo->target = -i - 1;
+-				break;
+-			}
+-		if (i == NUM_STANDARD_TARGETS)
++		if (FILL_TARGET(optarg, natinfo->target))
+ 			print_error("Illegal --snat-target target");
+ 		break;
+ 	default:
+@@ -106,7 +96,6 @@
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
+-	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 	struct ether_addr *addr;
+ 
+@@ -121,12 +110,7 @@
+ 		break;
+ 	case NAT_D_TARGET:
+ 		check_option(flags, OPT_DNAT_TARGET);
+-		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+-			if (!strcmp(optarg, standard_targets[i])) {
+-				natinfo->target = -i - 1;
+-				break;
+-			}
+-		if (i == NUM_STANDARD_TARGETS)
++		if (FILL_TARGET(optarg, natinfo->target))
+ 			print_error("Illegal --dnat-target target");
+ 		break;
+ 	default:
+@@ -137,14 +121,14 @@
+ 
+ static void final_check_s(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 
+-	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && natinfo->target == EBT_RETURN)
++	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+ 		print_error("--snat-target RETURN not allowed on base chain");
+-	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+-	if (!(hook_mask & (1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
++	CLEAR_BASE_CHAIN_BIT;
++	if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+ 		print_error("Wrong chain for snat");
+ 	if (time == 0 && to_source_supplied == 0)
+ 		print_error("No snat address supplied");
+@@ -152,16 +136,16 @@
+ 
+ static void final_check_d(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 
+-	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && natinfo->target == EBT_RETURN)
++	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+ 		print_error("--dnat-target RETURN not allowed on base chain");
+-	hook_mask &= ~(1 << NF_BR_NUMHOOKS);
+-	if (((hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) ||
+-	   strcmp(name, "nat")) &&
+-	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
++	CLEAR_BASE_CHAIN_BIT;
++	if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
++	   || strcmp(name, "nat")) &&
++	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+ 		print_error("Wrong chain for dnat");
+ 	if (time == 0 && to_dest_supplied == 0)
+ 		print_error("No dnat address supplied");
+@@ -174,7 +158,7 @@
+ 
+ 	printf("--to-src ");
+ 	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+-	printf(" --snat-target %s", standard_targets[-natinfo->target - 1]);
++	printf(" --snat-target %s", TARGET_NAME(natinfo->target));
+ }
+ 
+ static void print_d(const struct ebt_u_entry *entry,
+@@ -184,7 +168,7 @@
+ 
+ 	printf("--to-dst ");
+ 	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+-	printf(" --dnat-target %s", standard_targets[-natinfo->target - 1]);
++	printf(" --dnat-target %s", TARGET_NAME(natinfo->target));
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+@@ -221,7 +205,7 @@
+ 	final_check_d,
+ 	print_d,
+ 	compare,
+-	opts_d,
++	opts_d
+ };
+ 
+ static void _init(void) __attribute__ ((constructor));
+--- ebtables-v2.0-rc1/extensions/ebt_ip.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebt_ip.c	Thu Aug 29 18:53:30 2002
+@@ -1,7 +1,5 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <string.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+@@ -28,7 +26,8 @@
+ static int undot_ip(char *ip, unsigned char *ip2)
+ {
+ 	char *p, *q, *end;
+-	int onebyte, i;
++	long int onebyte;
++	int i;
+ 	char buf[20];
+ 
+ 	strncpy(buf, ip, sizeof(buf) - 1);
+@@ -46,7 +45,7 @@
+ 	}
+ 
+ 	onebyte = strtol(p, &end, 10);
+-	if (*end != '\0' || onebyte >255 || onebyte < 0)
++	if (*end != '\0' || onebyte > 255 || onebyte < 0)
+ 		return -1;
+ 	ip2[3] = (unsigned char)onebyte;
+ 
+@@ -57,8 +56,8 @@
+ static int ip_mask(char *mask, unsigned char *mask2)
+ {
+ 	char *end;
+-	int bits;
+-	__u32 mask22;
++	long int bits;
++	uint32_t mask22;
+ 
+ 	if (undot_ip(mask, mask2)) {
+ 		// not the /a.b.c.e format, maybe the /x format
+@@ -77,39 +76,38 @@
+ }
+ 
+ // set the ip mask and ip address
+-void parse_ip_address(char *address, __u32 *addr, __u32 *msk)
++void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+ {
+ 	char *p;
+-	int i;
+ 
+ 	// first the mask
+ 	if ((p = strrchr(address, '/')) != NULL) {
+ 		*p = '\0';
+-		i = ip_mask(p + 1, (unsigned char *)msk);
+-		if (i)
+-			print_error("Problem with the ip mask");
++		if (ip_mask(p + 1, (unsigned char *)msk))
++			print_error("Problem with the IP mask");
+ 	}
+ 	else
+ 		*msk = 0xFFFFFFFF;
+ 
+-	i = undot_ip(address, (unsigned char *)addr);
+-	if (i)
+-		print_error("Problem with the ip address");
++	if (undot_ip(address, (unsigned char *)addr))
++		print_error("Problem with the IP address");
+ 	*addr = *addr & *msk;
+ }
+ 
+ // transform the ip mask into a string ready for output
+-char *mask_to_dotted(__u32 mask)
++char *mask_to_dotted(uint32_t mask)
+ {
+ 	int i;
+ 	static char buf[20];
+-	__u32 maskaddr, bits;
++	uint32_t maskaddr, bits;
+ 
+ 	maskaddr = ntohl(mask);
+ 
+ 	// don't print /32
+-	if (mask == 0xFFFFFFFFL)
+-		return "";
++	if (mask == 0xFFFFFFFFL) {
++		*buf = '\0';
++		return buf;
++	}
+ 
+ 	i = 32;
+ 	bits = 0xFFFFFFFEL; // case 0xFFFFFFFF has just been dealt with
+@@ -156,7 +154,7 @@
+ {
+ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+ 	char *end;
+-	int i;
++	long int i;
+ 
+ 	switch (c) {
+ 	case IP_SOURCE:
+@@ -176,7 +174,7 @@
+ 		}
+ 
+ 		if (optind > argc)
+-			print_error("Missing ip address argument");
++			print_error("Missing IP address argument");
+ 		if (c == IP_SOURCE)
+ 			parse_ip_address(argv[optind - 1], &ipinfo->saddr,
+ 			   &ipinfo->smsk);
+@@ -191,10 +189,10 @@
+ 			ipinfo->invflags |= EBT_IP_TOS;
+ 
+ 		if (optind > argc)
+-			print_error("Missing ip tos argument");
++			print_error("Missing IP tos argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+ 		if (i < 0 || i > 255 || *end != '\0')
+-			print_error("Problem with specified ip tos");
++			print_error("Problem with specified IP tos");
+ 		ipinfo->tos = i;
+ 		ipinfo->bitmask |= EBT_IP_TOS;
+ 		break;
+@@ -204,10 +202,10 @@
+ 		if (check_inverse(optarg))
+ 			ipinfo->invflags |= EBT_IP_PROTO;
+ 		if (optind > argc)
+-			print_error("Missing ip protocol argument");
++			print_error("Missing IP protocol argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+ 		if (i < 0 || i > 255 || *end != '\0')
+-			print_error("Problem with specified ip protocol");
++			print_error("Problem with specified IP protocol");
+ 		ipinfo->protocol = i;
+ 		ipinfo->bitmask |= EBT_IP_PROTO;
+ 		break;
+@@ -219,10 +217,9 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_match *match, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+-	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+-	   entry->ethproto != ETH_P_IP)
++	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
+ 		print_error("For IP filtering the protocol must be "
+ 		            "specified as IPv4");
+ }
+@@ -259,7 +256,7 @@
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_PROTO) {
+ 		printf("--ip-proto ");
+-		if (ipinfo->invflags & EBT_IP_DEST)
++		if (ipinfo->invflags & EBT_IP_PROTO)
+ 			printf("! ");
+ 		printf("%d ", ipinfo->protocol);
+ 	}
+@@ -308,7 +305,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init(void) __attribute((constructor));
+--- ebtables-v2.0-rc1/extensions/ebt_arp.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebt_arp.c	Thu Aug 29 18:54:13 2002
+@@ -1,8 +1,6 @@
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_arp.h>
+@@ -23,40 +21,38 @@
+ 	{ 0 }
+ };
+ 
++#define NUMOPCODES 9
+ // a few names
+ static char *opcodes[] =
+ {
+ 	"Request",
+ 	"Reply",
+-	"Request Reverse",
+-	"Reply Reverse",
+-	"DRARP Request",
+-	"DRARP Reply",
+-	"DRARP Error",
+-	"InARP Request",
+-	"ARP NAK",
+-	""
++	"Request_Reverse",
++	"Reply_Reverse",
++	"DRARP_Request",
++	"DRARP_Reply",
++	"DRARP_Error",
++	"InARP_Request",
++	"ARP_NAK",
+ };
+ 
+ static void print_help()
+ {
+-	int i = 0;
++	int i;
+ 
+ 	printf(
+ "arp options:\n"
+ "--arp-opcode opcode            : ARP opcode (integer or string)\n"
+ "--arp-htype type               : ARP hardware type (integer or string)\n"
+ "--arp-ptype type               : ARP protocol type (hexadecimal or string)\n"
+-"--arp-ip-src [!] address[/mask]: ARP ip source specification\n"
+-"--arp-ip-dst [!] address[/mask]: ARP ip target specification\n"
++"--arp-ip-src [!] address[/mask]: ARP IP source specification\n"
++"--arp-ip-dst [!] address[/mask]: ARP IP target specification\n"
+ " opcode strings: \n");
+-	while (strcmp(opcodes[i], "")) {
++	for (i = 0; i < NUMOPCODES; i++)
+ 		printf("%d = %s\n", i + 1, opcodes[i]);
+-		i++;
+-	}
+ 	printf(
+-" hardware type string: \n 1 = Ethernet\n"
+-" protocol type string: \n 0x0800 = IPv4\n");
++" hardware type string: 1 = Ethernet\n"
++" protocol type string: see /etc/ethertypes\n");
+ }
+ 
+ static void init(struct ebt_entry_match *match)
+@@ -68,7 +64,7 @@
+ }
+ 
+ // defined in ebt_ip.c
+-void parse_ip_address(char *address, __u32 *addr, __u32 *msk);
++void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+ 
+ #define OPT_OPCODE 0x01
+ #define OPT_HTYPE  0x02
+@@ -79,10 +75,10 @@
+    unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+-	int i;
++	long int i;
+ 	char *end;
+-	__u32 *addr;
+-	__u32 *mask;
++	uint32_t *addr;
++	uint32_t *mask;
+ 
+ 	switch (c) {
+ 	case ARP_OPCODE:
+@@ -91,18 +87,16 @@
+ 			arpinfo->invflags |= EBT_ARP_OPCODE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp opcode argument");
++			print_error("Missing ARP opcode argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+ 		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+-			i = 0;
+-			while (strcmp(opcodes[i], "")) {
++			for (i = 0; i < NUMOPCODES; i++)
+ 				if (!strcasecmp(opcodes[i], optarg))
+ 					break;
+-				i++;
+-			}
+-			if (!strcmp(opcodes[i], ""))
++			if (i == NUMOPCODES)
+ 				print_error("Problem with specified "
+-				            "arp opcode");
++				            "ARP opcode");
++			i++;
+ 		}
+ 		arpinfo->opcode = htons(i);
+ 		arpinfo->bitmask |= EBT_ARP_OPCODE;
+@@ -114,13 +108,13 @@
+ 			arpinfo->invflags |= EBT_ARP_HTYPE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp hardware type argument");
++			print_error("Missing ARP hardware type argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+ 		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+ 			if (!strcasecmp("Ethernet", argv[optind - 1]))
+ 				i = 1;
+ 			else
+-				print_error("Problem with specified arp "
++				print_error("Problem with specified ARP "
+ 				            "hardware type");
+ 		}
+ 		arpinfo->htype = htons(i);
+@@ -128,23 +122,26 @@
+ 		break;
+ 
+ 	case ARP_PTYPE:
++	{
++		uint16_t proto;
++
+ 		check_option(flags, OPT_PTYPE);
+ 		if (check_inverse(optarg))
+ 			arpinfo->invflags |= EBT_ARP_PTYPE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp protocol type argument");
++			print_error("Missing ARP protocol type argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+ 		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+-			if (!strcasecmp("IPv4", argv[optind - 1]))
+-				i = 0x0800;
+-			else
+-				print_error("Problem with specified arp "
++			if (name_to_number (argv[optind - 1], &proto) == -1)
++				print_error("Problem with specified ARP "
+ 				            "protocol type");
+-		}
+-		arpinfo->ptype = htons(i);
++		} else
++			proto = i;
++		arpinfo->ptype = htons(proto);
+ 		arpinfo->bitmask |= EBT_ARP_PTYPE;
+ 		break;
++	}
+ 
+ 	case ARP_IP_S:
+ 	case ARP_IP_D:
+@@ -166,7 +163,7 @@
+ 				arpinfo->invflags |= EBT_ARP_DST_IP;
+ 		}
+ 		if (optind > argc)
+-			print_error("Missing ip address argument");
++			print_error("Missing ARP IP address argument");
+ 		parse_ip_address(argv[optind - 1], addr, mask);
+ 		break;
+ 	default:
+@@ -177,27 +174,33 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_match *match, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+-	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+-	   (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
++	if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) ||
++	    entry->invflags & EBT_IPROTO)
+ 		print_error("For (R)ARP filtering the protocol must be "
+ 		            "specified as ARP or RARP");
+ }
+ 
+ // defined in the ebt_ip.c
+-char *mask_to_dotted(__u32 mask);
++char *mask_to_dotted(uint32_t mask);
++
+ static void print(const struct ebt_u_entry *entry,
+    const struct ebt_entry_match *match)
+ {
+ 	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+ 	int i;
++	char name[21];
+ 
+ 	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
++		int opcode = ntohs(arpinfo->opcode);
+ 		printf("--arp-op ");
+ 		if (arpinfo->invflags & EBT_ARP_OPCODE)
+ 			printf("! ");
+-		printf("%d ", ntohs(arpinfo->opcode));
++		if (opcode > 0 && opcode <= NUMOPCODES)
++			printf("%s ", opcodes[opcode - 1]);
++		else
++			printf("%d ", opcode);
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_HTYPE) {
+ 		printf("--arp-htype ");
+@@ -209,7 +212,10 @@
+ 		printf("--arp-ptype ");
+ 		if (arpinfo->invflags & EBT_ARP_PTYPE)
+ 			printf("! ");
+-		printf("0x%x ", ntohs(arpinfo->ptype));
++		if (number_to_name(ntohs(arpinfo->ptype), name))
++			printf("0x%x ", ntohs(arpinfo->ptype));
++		else
++			printf("%s ", name);
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+ 		printf("--arp-ip-src ");
+@@ -278,7 +284,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init(void) __attribute__ ((constructor));
+--- ebtables-v2.0-rc1/extensions/ebt_vlan.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebt_vlan.c	Thu Aug 29 18:55:02 2002
+@@ -32,8 +32,6 @@
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <string.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+@@ -106,7 +104,7 @@
+ 	    (struct ebt_vlan_info *) (*match)->data;
+ 	unsigned long i;
+ 	char *end;
+-	__u16 encap;
++	uint16_t encap;
+ 	switch (c) {
+ 	case VLAN_ID:
+ 		/*
+@@ -128,7 +126,7 @@
+ 		/*
+ 		 * Convert argv to long int,
+ 		 * set *end to end of argv string, 
+-		 * base set 10 for decimal only 
++		 * base set 10 for decimal only
+ 		 */
+ 		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
+ 		/*
+@@ -217,15 +215,16 @@
+ static void
+ final_check (const struct ebt_u_entry *entry,
+ 	     const struct ebt_entry_match *match,
+-	     const char *name, unsigned int hook, unsigned int time)
++	     const char *name, unsigned int hookmask, unsigned int time)
+ {
+ 
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) match->data;
+ 	/*
+-	 * Is any proto param specified there? Or specified proto isn't 802.1Q?
++	 * Specified proto isn't 802.1Q?
+ 	 */
+-	if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
++	if (entry->ethproto != ETH_P_8021Q ||
++	    entry->invflags & EBT_IPROTO)
+ 		print_error
+ 		    ("For use 802.1Q extension the protocol must be specified as 802_1Q");
+ 	/*
+@@ -334,7 +333,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init (void) __attribute__ ((constructor));
+--- ebtables-v2.0-rc1/extensions/ebt_log.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebt_log.c	Sat Aug 24 15:29:50 2002
+@@ -1,7 +1,6 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <sys/socket.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_log.h>
+@@ -16,11 +15,12 @@
+ #define	LOG_NOTICE	5 // normal but significant condition
+ #define	LOG_INFO	6 // informational
+ #define	LOG_DEBUG	7 // debug-level messages
++
+ #define LOG_DEFAULT_LEVEL LOG_INFO
+ 
+ typedef struct _code {
+-	char	*c_name;
+-	int	c_val;
++	char *c_name;
++	int c_val;
+ } CODE;
+ 
+ static CODE eight_priority[] = {
+@@ -31,20 +31,16 @@
+ 	{ "warning", LOG_WARNING },
+ 	{ "notice", LOG_NOTICE },
+ 	{ "info", LOG_INFO },
+-	{ "debug", LOG_DEBUG },
+-	{ NULL, -1 }
++	{ "debug", LOG_DEBUG }
+ };
+ 
+ static int name_to_loglevel(char* arg)
+ {
+-	int i = 0, c_val = eight_priority[0].c_val;
++	int i;
+ 	
+-	while (c_val != -1) {
++	for (i = 0; i < 8; i++)
+ 		if (!strcmp(arg, eight_priority[i].c_name))
+-			return c_val;
+-		i++;
+-		c_val = eight_priority[i].c_val;
+-	}
++			return eight_priority[i].c_val;
+ 	// return bad loglevel
+ 	return 9;
+ }
+@@ -100,7 +96,7 @@
+    unsigned int *flags, struct ebt_entry_watcher **watcher)
+ {
+ 	struct ebt_log_info *loginfo = (struct ebt_log_info *)(*watcher)->data;
+-	int i;
++	long int i;
+ 	char *end;
+ 
+ 	switch (c) {
+@@ -143,7 +139,7 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_watcher *watcher, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ 	return;
+ }
+@@ -186,10 +182,9 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+-#undef _init
+ static void _init(void) __attribute__ ((constructor));
+ static void _init(void)
+ {
+--- ebtables-v2.0-rc1/extensions/ebt_standard.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebt_standard.c	Sat Aug 24 15:31:50 2002
+@@ -1,6 +1,5 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ 
+@@ -11,7 +10,8 @@
+ 
+ static void print_help()
+ {
+-	printf("Standard targets: DROP, ACCEPT and CONTINUE\n");
++	printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n"
++	       "The target can also be a user defined chain.\n");
+ }
+ 
+ static void init(struct ebt_entry_target *t)
+@@ -27,11 +27,12 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ }
+ 
+ struct ebt_u_entries *nr_to_chain(int nr);
++
+ static void print(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target)
+ {
+@@ -53,7 +54,7 @@
+ 	else if (verdict == EBT_RETURN)
+ 		printf("RETURN ");
+ 	else
+-		print_error("BUG: Bad standard target"); // this is a bug
++		print_bug("Bad standard target");
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0-rc1/extensions/ebtable_filter.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebtable_filter.c	Tue Aug 13 13:00:12 2002
+@@ -1,6 +1,4 @@
+ #include <stdio.h>
+-#include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include "../include/ebtables_u.h"
+ 
+ #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+--- ebtables-v2.0-rc1/extensions/ebtable_nat.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0-rc2/extensions/ebtable_nat.c	Tue Aug 13 12:59:14 2002
+@@ -1,5 +1,4 @@
+ #include <stdio.h>
+-#include <sys/socket.h>
+ #include "../include/ebtables_u.h"
+ 
+ #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+--- ebtables-v2.0-rc1/extensions/ebt_mark.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebt_mark.c	Sat Aug 24 15:30:09 2002
+@@ -1,21 +1,17 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <sys/socket.h>
+-#include <netinet/in.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_mark_t.h>
+ 
+-extern char *standard_targets[NUM_STANDARD_TARGETS];
++static int mark_supplied;
+ 
+-int mark_supplied;
+-
+-#define MARK_TARGET '1'
++#define MARK_TARGET  '1'
+ #define MARK_SETMARK '2'
+ static struct option opts[] =
+ {
+-	{ "mark-target"    , required_argument, 0, MARK_TARGET },
++	{ "mark-target" , required_argument, 0, MARK_TARGET },
+ 	{ "set-mark"    , required_argument, 0, MARK_SETMARK },
+ 	{ 0 }
+ };
+@@ -24,8 +20,8 @@
+ {
+ 	printf(
+ 	"mark target options:\n"
+-	" --set-mark value   : Set nfmark value\n"
+-	" --mark-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
++	" --set-mark value     : Set nfmark value\n"
++	" --mark-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
+ }
+ 
+ static void init(struct ebt_entry_target *target)
+@@ -36,16 +32,14 @@
+ 	markinfo->target = EBT_ACCEPT;
+ 	markinfo->mark = 0;
+ 	mark_supplied = 0;
+-	return;
+ }
+ 
+-#define OPT_MARK_TARGET  0x01
++#define OPT_MARK_TARGET   0x01
+ #define OPT_MARK_SETMARK  0x02
+ static int parse(int c, char **argv, int argc,
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
+-	int i;
+ 	struct ebt_mark_t_info *markinfo =
+ 	   (struct ebt_mark_t_info *)(*target)->data;
+ 	char *end;
+@@ -53,12 +47,7 @@
+ 	switch (c) {
+ 	case MARK_TARGET:
+ 		check_option(flags, OPT_MARK_TARGET);
+-		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+-			if (!strcmp(optarg, standard_targets[i])) {
+-				markinfo->target = -i - 1;
+-				break;
+-			}
+-		if (i == NUM_STANDARD_TARGETS)
++		if (FILL_TARGET(optarg, markinfo->target))
+ 			print_error("Illegal --mark-target target");
+ 		break;
+ 	case MARK_SETMARK:
+@@ -76,14 +65,14 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ 	struct ebt_mark_t_info *markinfo =
+ 	   (struct ebt_mark_t_info *)target->data;
+ 
+ 	if (time == 0 && mark_supplied == 0)
+ 		print_error("No mark value supplied");
+-	if ((hook_mask & (1 << NF_BR_NUMHOOKS)) && markinfo->target == EBT_RETURN)
++	if (BASE_CHAIN && markinfo->target == EBT_RETURN)
+ 		print_error("--mark-target RETURN not allowed on base chain");
+ }
+ 
+@@ -96,8 +85,7 @@
+ 	printf("--set-mark 0x%lx", markinfo->mark);
+ 	if (markinfo->target == EBT_ACCEPT)
+ 		return;
+-	printf(" --mark-target %s",
+-	   standard_targets[-markinfo->target - 1]);
++	printf(" --mark-target %s", TARGET_NAME(markinfo->target));
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+@@ -122,7 +110,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init(void) __attribute__ ((constructor));
+--- ebtables-v2.0-rc1/extensions/ebt_mark_m.c	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/extensions/ebt_mark_m.c	Sat Aug 24 15:30:26 2002
+@@ -1,6 +1,5 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+-#include <sys/socket.h>
+ #include <string.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+@@ -25,9 +24,9 @@
+ {
+ 	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)match->data;
+ 
+-	markinfo->mark = 0;
+-	markinfo->mask = 0;
+-	markinfo->invert = 0;
++	markinfo->mark    = 0;
++	markinfo->mask    = 0;
++	markinfo->invert  = 0;
+ 	markinfo->bitmask = 0;
+ }
+ 
+@@ -65,7 +64,7 @@
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+    const struct ebt_entry_match *match, const char *name,
+-   unsigned int hook_mask, unsigned int time)
++   unsigned int hookmask, unsigned int time)
+ {
+ }
+ 
+@@ -113,7 +112,7 @@
+ 	final_check,
+ 	print,
+ 	compare,
+-	opts,
++	opts
+ };
+ 
+ static void _init(void) __attribute((constructor));
+--- ebtables-v2.0-rc1/ChangeLog	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/ChangeLog	Fri Aug 30 22:40:11 2002
+@@ -1,3 +1,10 @@
++20020830
++	* updated the kernel files for 2.4.20-pre5 and 2.5.32
++	* last big cleanup of kernel and userspace code just finished
++20020820
++	* ARP module bugfix
++	* IP module bugfix
++	* nat module bugfix
+ 20020730
+ 	* other things done before 2.0-rc1 that I can think of,
+ 	  including kernel:
+--- ebtables-v2.0-rc1/ebtables.8	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/ebtables.8	Sun Aug 11 14:00:57 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "23 July 2002"
++.TH EBTABLES 8  "11 August 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -21,7 +21,7 @@
+ .\"     
+ .\"
+ .SH NAME
+-ebtables (v.2.0) \- Ethernet bridge packet table administration
++ebtables (v.2.0) \- Ethernet bridge frame table administration
+ .SH SYNOPSIS
+ .BR "ebtables -[ADI] " "chain rule-specification " [ options ]
+ .br
+@@ -33,10 +33,6 @@
+ .br
+ .BR "ebtables -E " "old-chain-name new-chain-name"
+ .br
+-.B "ebtables -L DB"
+-.br
+-.BR "ebtables -[b] [" "y/n" "]"
+-.br
+ .BR "ebtables --init-table"
+ .br
+ .BR "ebtables --atomic-init " file
+@@ -147,18 +143,9 @@
+ .TP
+ .B "-L, --list"
+ List all rules in the selected chain. If no chain is selected, all chains
+-are listed. If the chainname equals 
+-.BR DB ,
+-.B ebtables
+-will try to show the database. This database gives a survey of the kind of
+-frames that pass the different bridge hooks. It uses the interfaces where
+-the frame came in or will go out, the protocol field and the hook. This
+-database is independent from the rest of
+-.B ebtables
+-and is in a different kernel module.
++are listed.
+ .br
+-The following three options change the output when not listing the
+-database:
++The following three options change the output:
+ .br
+ .B "--Ln"
+ .br
+@@ -196,7 +183,8 @@
+ .BR DROP .
+ .TP
+ .B "-N, --new-chain"
+-Create a new user-defined chain by the given name.
++Create a new user-defined chain by the given name. The number of
++user-defined chains is unlimited. A chain name has max length of 31.
+ .TP
+ .B "-X, --delete-chain"
+ Delete the specified user-defined chain. There must be no references to the
+@@ -335,16 +323,15 @@
+ .B ebtables
+ will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
+ .TP
+-.BR "-b --db " [ "y/n" ]
+-Enable (y) or disable (n) the database.
+-.TP
+ .BR "-j, --jump " "\fItarget\fP"
+ The target of the rule. This is one of the following values:
+ .BR ACCEPT ,
+ .BR DROP ,
+ .BR CONTINUE ,
+-or a target extension, see
+-.BR "TARGET EXTENSIONS" .
++.BR RETURN ,
++a target extension (see
++.BR "TARGET EXTENSIONS" ")"
++or a user defined chain name.
+ .TP
+ .B --atomic file
+ Let the command operate on the specified file. The data of the table to
+--- ebtables-v2.0-rc1/ethertypes	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/ethertypes	Sun Aug 11 18:55:35 2002
+@@ -5,28 +5,28 @@
+ # always put tabs or spaces between the name and the protocol number
+ # anything on a line after the protocol number is ignored
+ # programs using this file should not be case sensitive
+-IPv4 	0800
+-X25	0805
+-ARP	0806
+-802_1Q	8100	802.1Q Virtual LAN tagged frame
+-IPX	8137
+-IPv6	86DD
+-NetBEUI	8191
+-BPQ	08FF	G8BPQ AX.25 Ethernet Packet
+-DEC	6000	DEC Assigned proto
+-DNA_DL	6001	DEC DNA Dump/Load
+-DNA_RC	6002	DEC DNA Remote Console
+-DNA_RT	6003	DEC DNA Routing
+-LAT	6004	DEC LAT
+-DIAG	6005	DEC Diagnostics
+-CUST	6006	DEC Customer use
+-SCA	6007	DEC Systems Comms Arch
+-RARP	8035	Reverse Addr Res packet
+-ATALK	809B	Appletalk DDP
+-AARP	80F3	Appletalk AARP
+-IPX	8137	IPX over DIX
++IPv4	 	0800
++X25		0805
++ARP		0806
++802_1Q		8100	802.1Q Virtual LAN tagged frame
++IPX		8137
++IPv6		86DD
++NetBEUI		8191
++BPQ		08FF	G8BPQ AX.25 Ethernet Packet
++DEC		6000	DEC Assigned proto
++DNA_DL		6001	DEC DNA Dump/Load
++DNA_RC		6002	DEC DNA Remote Console
++DNA_RT		6003	DEC DNA Routing
++LAT		6004	DEC LAT
++DIAG		6005	DEC Diagnostics
++CUST		6006	DEC Customer use
++SCA		6007	DEC Systems Comms Arch
++RARP		8035	Reverse Addr Res packet
++ATALK		809B	Appletalk DDP
++AARP		80F3	Appletalk AARP
++IPX		8137	IPX over DIX
+ PPP_DISC	8863	PPPoE discovery messages
+-PPP_SES	8864	PPPoE session messages
+-ATMMPOA	884C	MultiProtocol over ATM
+-ATMFATE	8884	Frame-based ATM Transport over Ethernet
+-LOOP	9000
++PPP_SES		8864	PPPoE session messages
++ATMMPOA		884C	MultiProtocol over ATM
++ATMFATE		8884	Frame-based ATM Transport over Ethernet
++LOOP		9000
+--- ebtables-v2.0-rc1/include/ebtables_u.h	Wed Jul 31 21:55:02 2002
++++ ebtables-v2.0-rc2/include/ebtables_u.h	Thu Aug 29 18:58:36 2002
+@@ -23,8 +23,8 @@
+ 
+ #ifndef EBTABLES_U_H
+ #define EBTABLES_U_H
++#include <netinet/in.h>
+ #include <linux/netfilter_bridge/ebtables.h>
+-#include <linux/br_db.h>
+ 
+ struct ebt_u_entries
+ {
+@@ -75,7 +75,7 @@
+ struct ebt_u_table
+ {
+ 	char name[EBT_TABLE_MAXNAMELEN];
+-	int (*check)(struct ebt_u_replace *repl);
++	void (*check)(struct ebt_u_replace *repl);
+ 	void (*help)(char **);
+ 	struct ebt_u_table *next;
+ };
+@@ -96,7 +96,7 @@
+ {
+ 	unsigned int bitmask;
+ 	unsigned int invflags;
+-	__u16 ethproto;
++	uint16_t ethproto;
+ 	char in[IFNAMSIZ];
+ 	char logical_in[IFNAMSIZ];
+ 	char out[IFNAMSIZ];
+@@ -114,7 +114,7 @@
+ struct ebt_u_match
+ {
+ 	char name[EBT_FUNCTION_MAXNAMELEN];
+-	// size of the real match data + sizeof struct ebt_match
++	// size of the real match data
+ 	unsigned int size;
+ 	void (*help)(void);
+ 	void (*init)(struct ebt_entry_match *m);
+@@ -123,7 +123,7 @@
+ 	        struct ebt_entry_match **match);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_match *match,
+-	   const char *name, unsigned int hook_mask, unsigned int time);
++	   const char *name, unsigned int hookmask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_match *match);
+ 	int (*compare)(const struct ebt_entry_match *m1,
+@@ -150,7 +150,7 @@
+ 	   struct ebt_entry_watcher **watcher);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_watcher *watch, const char *name,
+-	   unsigned int hook_mask, unsigned int time);
++	   unsigned int hookmask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_watcher *watcher);
+ 	int (*compare)(const struct ebt_entry_watcher *w1,
+@@ -174,7 +174,7 @@
+ 	   struct ebt_entry_target **target);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_target *target, const char *name,
+-	   unsigned int hook_mask, unsigned int time);
++	   unsigned int hookmask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_target *target);
+ 	int (*compare)(const struct ebt_entry_target *t1,
+@@ -198,21 +198,17 @@
+ struct ebt_u_table *find_table(char *name);
+ void deliver_counters(struct ebt_u_replace *repl);
+ void deliver_table(struct ebt_u_replace *repl);
+-void get_dbinfo(struct brdb_dbinfo *nr);
+-void get_db(int len, struct brdb_dbentry *db);
+-void deliver_allowdb(__u16 *decision);
+-int name_to_number(char *name, __u16 *proto);
++int name_to_number(char *name, uint16_t *proto);
+ int number_to_name(unsigned short proto, char *name);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
++void __print_bug(char *file, int line, char *format, ...);
+ #define print_bug(format, args...) \
+-   {printf("BUG: "format".\n", ##args); exit(-1);}
++   __print_bug(__FILE__, __LINE__, format, ##args)
+ #define print_error(format, args...) {printf(format".\n", ##args); exit(-1);}
+ #define print_memory() {printf("Ebtables: " __FILE__ " " __FUNCTION__ \
+    " %d :Out of memory.\n", __LINE__); exit(-1);}
+ 
+-
+-
+ // used for keeping the rule counters right during rule adds or deletes
+ #define CNT_NORM 0
+ #define CNT_DEL 1
+@@ -220,4 +216,27 @@
+ #define CNT_END 3
+ #define CNT_ZERO 4
+ 
++extern char *standard_targets[NUM_STANDARD_TARGETS];
++// Transforms a target string into the right integer,
++// returns 0 on success.
++#define FILL_TARGET(_str, _pos) ({                        \
++	int _i, _ret = 0;                                 \
++	for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++)     \
++		if (!strcmp(_str, standard_targets[_i])) {\
++			_pos = -_i - 1;                   \
++			break;                            \
++		}                                         \
++	if (_i == NUM_STANDARD_TARGETS)                   \
++		_ret = 1;                                 \
++	_ret;                                             \
++})
++
++// Transforms the target value to an index into standard_targets[]
++#define TARGET_INDEX(_value) (-_value - 1)
++// Returns a target string corresponding to the value
++#define TARGET_NAME(_value) (standard_targets[TARGET_INDEX(_value)])
++// True if the hook mask denotes that the rule is in a base chain
++#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
++// Clear the bit in the hook_mask that tells if the rule is on a base chain
++#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+ #endif /* EBTABLES_U_H */
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0.001.diff
new file mode 100644
index 0000000..85aee69
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0.001.diff
@@ -0,0 +1,17 @@
+--- ebtables-v2.0-rc2/Makefile	Sat Aug 31 11:51:29 2002
++++ ebtables-v2.0/Makefile	Thu Sep 19 19:52:09 2002
+@@ -2,11 +2,12 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0-rc2"
+-PROGDATE:="August 2002"
++PROGVERSION:="2.0"
++PROGDATE:="September 2002"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
++CC:=gcc
+ include extensions/Makefile
+ 
+ # Some kernel testers prefer to use a symlink for /usr/include/linux
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre10.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre10.001.diff
new file mode 100644
index 0000000..55ee566
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre10.001.diff
@@ -0,0 +1,802 @@
+--- ebtables-v2.0pre9/Makefile	Sun Jul  7 16:29:50 2002
++++ ebtables-v2.0pre10.001/Makefile	Wed Jul 10 22:12:36 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre9 (July 2002)"
++PROGVERSION:="2.0pre10 (July 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -51,9 +51,12 @@
+ /etc/ethertypes: ethertypes
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
++.PHONY: exec
++exec: ebtables
++	install -m 0755 -o root -g root $< /sbin/ebtables
+ 
+ install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
+-	ebtables /etc/ethertypes
++	ebtables /etc/ethertypes exec
+ 
+ clean:
+ 	rm -f ebtables
+--- ebtables-v2.0pre9/ebtables.c	Sat Jun 29 11:41:57 2002
++++ ebtables-v2.0pre10.001/ebtables.c	Tue Jul 16 20:36:50 2002
+@@ -62,11 +62,17 @@
+ };
+ 
+ // default command line options
+-static struct option ebt_original_options[] = {
++// do not mess around with the already assigned numbers unless
++// you know what you are doing
++static struct option ebt_original_options[] =
++{
+ 	{ "append"        , required_argument, 0, 'A' },
+ 	{ "insert"        , required_argument, 0, 'I' },
+ 	{ "delete"        , required_argument, 0, 'D' },
+ 	{ "list"          , optional_argument, 0, 'L' },
++	{ "Lc"            , no_argument      , 0, 4   },
++	{ "Ln"            , no_argument      , 0, 5   },
++	{ "Lx"            , no_argument      , 0, 6   },
+ 	{ "zero"          , optional_argument, 0, 'Z' },
+ 	{ "flush"         , optional_argument, 0, 'F' },
+ 	{ "policy"        , required_argument, 0, 'P' },
+@@ -91,13 +97,19 @@
+ 	{ "new-chain"     , required_argument, 0, 'N' },
+ 	{ "rename-chain"  , required_argument, 0, 'E' },
+ 	{ "delete-chain"  , required_argument, 0, 'X' },
++	{ "atomic-init"   , required_argument, 0, 7   },
++	{ "atomic-commit" , required_argument, 0, 8   },
++	{ "atomic"        , required_argument, 0, 9   },
++	{ "atomic-save"   , required_argument, 0, 10  },
++	{ "init-table"    , no_argument      , 0, 11  },
+ 	{ 0 }
+ };
+ 
+ static struct option *ebt_options = ebt_original_options;
+ 
+ // yup, all the possible target names
+-char* standard_targets[NUM_STANDARD_TARGETS] = {
++char* standard_targets[NUM_STANDARD_TARGETS] =
++{
+ 	"ACCEPT",
+ 	"DROP",
+ 	"CONTINUE",
+@@ -169,7 +181,7 @@
+ // Same holds for the struct ebt_match and struct ebt_watcher pointers
+ struct ebt_u_entry *new_entry;
+ 
+-void initialize_entry(struct ebt_u_entry *e)
++static void initialize_entry(struct ebt_u_entry *e)
+ {
+ 	e->bitmask = EBT_NOPROTO;
+ 	e->invflags = 0;
+@@ -188,7 +200,7 @@
+ }
+ 
+ // this doesn't free e, becoz the calling function might need e->next
+-void free_u_entry(struct ebt_u_entry *e)
++static void free_u_entry(struct ebt_u_entry *e)
+ {
+ 	struct ebt_u_match_list *m_l, *m_l2;
+ 	struct ebt_u_watcher_list *w_l, *w_l2;
+@@ -403,7 +415,7 @@
+ 
+ 
+ // used to parse /etc/ethertypes
+-int disregard_whitespace(char *buffer, FILE *ifp)
++static int disregard_whitespace(char *buffer, FILE *ifp)
+ {
+ 	int hlp;
+ 
+@@ -416,7 +428,7 @@
+ }
+ 
+ // used to parse /etc/ethertypes
+-int disregard_tabspace(char *buffer, FILE *ifp)
++static int disregard_tabspace(char *buffer, FILE *ifp)
+ {
+ 	int hlp;
+ 
+@@ -429,7 +441,7 @@
+ }
+ 
+ // helper function: processes a line of data from the file /etc/ethertypes
+-int get_a_line(char *buffer, char *value, FILE *ifp)
++static int get_a_line(char *buffer, char *value, FILE *ifp)
+ {
+ 	int i, hlp;
+ 	char anotherhlp;
+@@ -507,6 +519,11 @@
+ 	}
+ }
+ 
++// we use replace.flags, so we can't use the following values:
++// 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
++#define LIST_N 0x04
++#define LIST_C 0x08
++#define LIST_X 0x10
+ // helper function for list_rules()
+ static void list_em(struct ebt_u_entries *entries)
+ {
+@@ -520,9 +537,14 @@
+ 	char name[21];
+ 
+ 	hlp = entries->entries;
+-	printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
+-	   standard_targets[-entries->policy - 1]);
+-	printf("nr. of entries: %d \n", entries->nentries);
++	if (replace.flags & LIST_X && entries->policy != EBT_ACCEPT) {
++		printf("ebtables -t %s -P %s %s\n", replace.name,
++		   entries->name, standard_targets[-entries->policy - 1]);
++	} else if (!(replace.flags & LIST_X)) {
++		printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
++		   standard_targets[-entries->policy - 1]);
++		printf("nr. of entries: %d \n", entries->nentries);
++	}
+ 
+ 	i = entries->nentries;
+ 	while (i > 9) {
+@@ -531,16 +553,21 @@
+ 	}
+ 
+ 	for (i = 0; i < entries->nentries; i++) {
+-		digits = 0;
+-		// A little work to get nice rule numbers.
+-		j = i + 1;
+-		while (j > 9) {
+-			digits++;
+-			j /= 10;
+-		}
+-		for (j = 0; j < space - digits; j++)
+-			printf(" ");
+-		printf("%d. ", i + 1);
++		if (replace.flags & LIST_N) {
++			digits = 0;
++			// A little work to get nice rule numbers.
++			j = i + 1;
++			while (j > 9) {
++				digits++;
++				j /= 10;
++			}
++			for (j = 0; j < space - digits; j++)
++				printf(" ");
++			printf("%d. ", i + 1);
++		}
++		if (replace.flags & LIST_X)
++			printf("ebtables -t %s -A %s ",
++			   replace.name, entries->name);
+ 
+ 		// Don't print anything about the protocol if no protocol was
+ 		// specified, obviously this means any protocol will do.
+@@ -668,8 +695,9 @@
+ 		if (!t)
+ 			print_bug("Target not found");
+ 		t->print(hlp, hlp->t);
+-		printf(", count = %llu",
+-		   replace.counters[entries->counter_offset + i].pcnt);
++		if (replace.flags & LIST_C)
++			printf(", count = %llu",
++			   replace.counters[entries->counter_offset + i].pcnt);
+ 		printf("\n");
+ 		hlp = hlp->next;
+ 	}
+@@ -710,7 +738,7 @@
+ 	struct ebt_u_entries *entries;
+ };
+ 
+-void check_for_loops()
++static void check_for_loops()
+ {
+ 	int chain_nr , i, j , k, sp = 0, verdict;
+ 	struct ebt_u_entries *entries, *entries2;
+@@ -813,7 +841,7 @@
+ }
+ 
+ // yup, print out help
+-void print_help()
++static void print_help()
+ {
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+@@ -833,11 +861,16 @@
+ "--list   -L [chain]           : List the rules in a chain or in all chains\n"
+ "--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
+ "--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
++"--init-table                  : Replace the kernel table with the initial table\n"
+ "--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
+ "--policy -P chain target      : Change policy on chain to target\n"
+ "--new-chain -N chain          : Create a user defined chain\n"
+ "--rename-chain -E old new     : Rename a chain\n"
+ "--delete-chain -X chain       : Delete a user defined chain\n"
++"--atomic-commit file          : update the kernel w/ the table contained in file\n"
++"--atomic-init file            : put the initial kernel table into file\n"
++"--atomic-save file            : put the current kernel table into file\n"
++"--atomic file                 : write changes to file instead of kernel\n"
+ "Options:\n"
+ "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+ "--src    -s [!] address[/mask]: source mac address\n"
+@@ -846,7 +879,7 @@
+ "--out-if -o [!] name          : network output interface name\n"
+ "--logical-in  [!] name        : logical bridge input interface name\n"
+ "--logical-out [!] name        : logical bridge output interface name\n"
+-"--modprobe -M                 : try to insert modules using this command\n"
++"--modprobe -M program         : try to insert modules using this program\n"
+ "--version -V                  : print package version\n"
+ "\n" ,
+ 	prog_name,
+@@ -876,12 +909,28 @@
+ {
+ 	int i;
+ 
+-	printf("Bridge table: %s\n", table->name);
++	if (!(replace.flags & LIST_X))
++		printf("Bridge table: %s\n", table->name);
+ 	if (replace.selected_hook != -1) {
+ 		list_em(to_chain());
+ 	} else {
+ 		struct ebt_u_chain_list *cl = replace.udc;
+ 
++		// create new chains and rename standard chains when necessary
++		if (replace.flags & LIST_X) {
++			while (cl) {
++				printf("ebtables -t %s -N %s\n", replace.name,
++				   cl->udc->name);
++				cl = cl->next;
++			}
++			cl = replace.udc;
++			for (i = 0; i < NF_BR_NUMHOOKS; i++)
++				if (replace.valid_hooks & (1 << i) &&
++				   strcmp(replace.hook_entry[i]->name, hooknames[i]))
++					printf("ebtables -t %s -E %s %s\n",
++					   replace.name, hooknames[i],
++					   replace.hook_entry[i]->name);
++		}
+ 		i = 0;
+ 		while (1) {
+ 			if (i < NF_BR_NUMHOOKS) {
+@@ -1292,7 +1341,7 @@
+ }
+ 
+ // execute command Z
+-void zero_counters(int zerochain)
++static void zero_counters(int zerochain)
+ {
+ 
+ 	if (zerochain == -1) {
+@@ -1463,7 +1512,7 @@
+ }
+ 
+ // executes the final_check() function for all extensions used by the rule
+-void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
++static void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
+ {
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+@@ -1491,7 +1540,7 @@
+ }
+ 
+ // used for the -X command
+-void check_for_references(int chain_nr)
++static void check_for_references(int chain_nr)
+ {
+ 	int i = -1, j;
+ 	struct ebt_u_entries *entries;
+@@ -1571,6 +1620,7 @@
+ 	replace.flags = 0;
+ 	replace.selected_hook = -1;
+ 	replace.command = 'h';
++	replace.filename = NULL;
+ 
+ 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ 	if (!new_entry)
+@@ -1579,7 +1629,8 @@
+ 	initialize_entry(new_entry);
+ 
+ 	// The scenario induced by this loop makes that:
+-	// '-t'  and '-M' (if specified) have to come before '-A' and the like
++	// '-t'  ,'-M' and --atomic (if specified) have to come
++	// before '-A' and the like
+ 
+ 	// getopt saves the day
+ 	while ((c = getopt_long(argc, argv,
+@@ -2013,8 +2064,90 @@
+ 			allowbc = *optarg;
+ 			break;
+ 
+-		default:
++		case 4  : // Lc
++			check_option(&replace.flags, LIST_C);
++			if (replace.selected_hook == DATABASEHOOKNR)
++				print_error("--Lc not valid for listing"
++				   " the database");
++			if (replace.command != 'L')
++				print_error("Use --Lc with -L");
++			if (replace.flags & LIST_X)
++				print_error("--Lx not compatible with --Lc");
++			replace.flags |= LIST_C;
++			break;
++		case 5  : // Ln
++			check_option(&replace.flags, LIST_N);
++			if (replace.selected_hook == DATABASEHOOKNR)
++				print_error("--Ln not valid for listing"
++				   " the database");
++			if (replace.command != 'L')
++				print_error("Use --Ln with -L");
++			if (replace.flags & LIST_X)
++				print_error("--Lx not compatible with --Ln");
++			replace.flags |= LIST_N;
++			break;
++		case 6  : // Lx
++			check_option(&replace.flags, LIST_X);
++			if (replace.selected_hook == DATABASEHOOKNR)
++				print_error("--Lx not valid for listing"
++				   " the database");
++			if (replace.command != 'L')
++				print_error("Use --Lx with -L");
++			if (replace.flags & LIST_C)
++				print_error("--Lx not compatible with --Lc");
++			if (replace.flags & LIST_N)
++				print_error("--Lx not compatible with --Ln");
++			replace.flags |= LIST_X;
++			break;
++		case 8 : // atomic-commit
++			replace.command = c;
++			if (replace.flags & OPT_COMMAND)
++				print_error("Multiple commands not allowed");
++			replace.flags |= OPT_COMMAND;
++			replace.filename = (char *)malloc(strlen(optarg) + 1);
++			strcpy(replace.filename, optarg);
++			// get the information from the file
++			get_table(&replace);
++                        if (replace.nentries) {
++                                counterchanges = (unsigned short *)
++                                   malloc(sizeof(unsigned short) * (replace.nentries + 1));
++				for (i = 0; i < replace.nentries; i++)
++                                        counterchanges[i] = CNT_NORM;
++                                counterchanges[i] = CNT_END;
++                        }
++			free(replace.filename);
++			replace.filename = NULL;
++			break;
++		case 7 : // atomic-init
++		case 10: // atomic-save
++		case 11: // init-table
++			replace.command = c;
++			if (replace.flags & OPT_COMMAND)
++				print_error("Multiple commands not allowed");
++			replace.flags |= OPT_COMMAND;
++			if ( !(table = find_table(replace.name)) )
++				print_error("Bad table name");
++			if (get_table(&replace)) {
++				ebtables_insmod("ebtables", modprobe);
++				if (get_table(&replace))
++					print_error("can't initialize ebtables "
++					"table %s", replace.name);
++			}
++			if (replace.nentries) {
++				counterchanges = (unsigned short *)
++				   malloc(sizeof(unsigned short) * (replace.nentries + 1));
++				for (i = 0; i < replace.nentries; i++)
++					counterchanges[i] = CNT_NORM;
++				counterchanges[i] = CNT_END;
++			}
++			if (c == 11)
++				break;
++		case 9 : // atomic
++			replace.filename = (char *)malloc(strlen(optarg) + 1);
++			strcpy(replace.filename, optarg);
++			break;
+ 
++		default:
+ 			// is it a target option?
+ 			t = (struct ebt_u_target *)new_entry->t;
+ 			if ((t->parse(c - t->option_offset, argv, argc,
+@@ -2142,7 +2275,8 @@
+ 		}
+ 	} else if (replace.command == 'D')
+ 		delete_rule(rule_nr);
+-	// commands -N, -E, -X fall through
++	// commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
++	// --init-table fall through
+ 
+ 	if (table->check)
+ 		table->check(&replace);
+--- ebtables-v2.0pre9/communication.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre10.001/communication.c	Mon Jul 15 22:35:14 2002
+@@ -27,7 +27,7 @@
+ 
+ int sockfd = -1;
+ 
+-void get_sockfd()
++static void get_sockfd()
+ {
+ 	if (sockfd == -1) {
+ 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+@@ -209,6 +209,47 @@
+ 	return new;
+ }
+ 
++static void store_table_in_file(char *filename, struct ebt_replace *repl)
++{
++	char *command, *data;
++	int size;
++	FILE *file;
++
++	// start from an empty file with right priviliges
++	command = (char *)malloc(strlen(filename) + 15);
++	if (!command)
++		print_memory();
++	strcpy(command, "cat /dev/null>");
++	strcpy(command + 14, filename);
++	if (system(command))
++		print_error("Couldn't create file %s", filename);
++	strcpy(command, "chmod 600 ");
++	strcpy(command + 10, filename);
++	if (system(command))
++		print_error("Couldn't chmod file %s", filename);
++	free(command);
++
++	size = sizeof(struct ebt_replace) + repl->entries_size +
++	   repl->nentries * sizeof(struct ebt_counter);
++	data = (char *)malloc(size);
++	if (!data)
++		print_memory();
++	memcpy(data, repl, sizeof(struct ebt_replace));
++	memcpy(data + sizeof(struct ebt_replace), repl->entries,
++	   repl->entries_size);
++	// initialize counters to zero, deliver_counters() can update them
++	memset(data + sizeof(struct ebt_replace) + repl->entries_size,
++	   0, repl->nentries * sizeof(struct ebt_counter));
++	if (!(file = fopen(filename, "wb")))
++		print_error("Couldn't open file %s", filename);
++	if (fwrite(data, sizeof(char), size, file) != size) {
++		fclose(file);
++		print_error("Couldn't write everything to file %s", filename);
++	}
++	fclose(file);
++	free(data);
++}
++
+ void deliver_table(struct ebt_u_replace *u_repl)
+ {
+ 	socklen_t optlen;
+@@ -216,15 +257,43 @@
+ 
+ 	// translate the struct ebt_u_replace to a struct ebt_replace
+ 	repl = translate_user2kernel(u_repl);
+-	get_sockfd();
+ 	// give the data to the kernel
+ 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
++	if (u_repl->filename != NULL) {
++		store_table_in_file(u_repl->filename, repl);
++		return;
++	}
++	get_sockfd();
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+ 		print_error("The kernel doesn't support a certain ebtables"
+ 		  " extension, consider recompiling your kernel or insmod"
+ 		  " the extension");	
+ }
+ 
++static void store_counters_in_file(char *filename, struct ebt_u_replace *repl)
++{
++	int size = repl->nentries * sizeof(struct ebt_counter);
++	int entries_size;
++	struct ebt_replace hlp;
++	FILE *file;
++
++	if (!(file = fopen(filename, "r+b")))
++		print_error("Could not open file %s", filename);
++	// find out entries_size and then set the file pointer to the counters
++	if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
++	   || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
++	   sizeof(unsigned int) ||
++	   fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) {
++		fclose(file);
++		print_error("File %s is corrupt", filename);
++	}
++	if (fwrite(repl->counters, sizeof(char), size, file) != size) {
++		fclose(file);
++		print_error("Could not write everything to file %s", filename);
++	}
++	fclose(file);
++}
++
+ // gets executed after deliver_table
+ void
+ deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
+@@ -273,6 +342,10 @@
+ 	free(u_repl->counters);
+ 	u_repl->counters = newcounters;
+ 	u_repl->num_counters = u_repl->nentries;
++	if (u_repl->filename != NULL) {
++		store_counters_in_file(u_repl->filename, u_repl);
++		return;
++	}
+ 	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
+ 	   sizeof(struct ebt_replace);
+ 	// now put the stuff in the kernel's struct ebt_replace
+@@ -484,37 +557,119 @@
+ 	return 0;
+ }
+ 
+-// talk with kernel to receive the kernel's table
+-int get_table(struct ebt_u_replace *u_repl)
++static void retrieve_from_file(char *filename, struct ebt_replace *repl,
++   char command)
+ {
+-	int i, j, k, hook;
+-	socklen_t optlen;
+-	struct ebt_replace repl;
+-	struct ebt_u_entry **u_e;
++	FILE *file;
++	char *hlp;
++	int size;
++
++	if (!(file = fopen(filename, "r+b")))
++		print_error("Could not open file %s", filename);
++	// make sure table name is right if command isn't -L or --atomic-commit
++	if (command != 'L' && command != 8) {
++		hlp = (char *)malloc(strlen(repl->name));
++		if (!hlp)
++			print_memory();
++		strcpy(hlp, repl->name);
++	}
++	if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
++	   != sizeof(struct ebt_replace))
++		print_error("File %s is corrupt", filename);
++	if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) {
++		fclose(file);
++		print_error("File %s contains wrong table name or is corrupt",
++		   filename);
++	} else
++		if (!find_table(repl->name)) {
++			fclose(file);
++			print_error("File %s contains invalid table name",
++			   filename);
++		}
+ 
+-	get_sockfd();
++	size = sizeof(struct ebt_replace) +
++	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
++	fseek(file, 0, SEEK_END);
++	if (size != ftell(file)) {
++		fclose(file);
++		print_error("File %s has wrong size", filename);
++	}
++	repl->entries = (char *)malloc(repl->entries_size);
++	if (!repl->entries)
++		print_memory();
++	if (repl->nentries) {
++		repl->counters = (struct ebt_counter *)
++		   malloc(repl->nentries * sizeof(struct ebt_counter));
++		if (!repl->counters)
++			print_memory();
++	} else
++		repl->counters = NULL;
++	// copy entries and counters
++	if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
++	   fread(repl->entries, sizeof(char), repl->entries_size, file)
++	   != repl->entries_size ||
++	   fseek(file, sizeof(struct ebt_replace) + repl->entries_size, SEEK_SET)
++	   || fread(repl->counters, sizeof(char),
++	   repl->nentries * sizeof(struct ebt_counter), file)
++	   != repl->nentries * sizeof(struct ebt_counter)) {
++		fclose(file);
++		print_error("File %s is corrupt", filename);
++	}
++	fclose(file);
++}
++
++static int retrieve_from_kernel(struct ebt_replace *repl, char command)
++{
++	socklen_t optlen;
++	int optname;
+ 
+ 	optlen = sizeof(struct ebt_replace);
+-	strcpy(repl.name, u_repl->name);
+-	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
++	get_sockfd();
++	// --atomic-init || --init-table
++	if (command == 7 || command == 11)
++		optname = EBT_SO_GET_INIT_INFO;
++	else
++		optname = EBT_SO_GET_INFO;
++	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+ 		return -1;
+ 
+-	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
++	if ( !(repl->entries = (char *) malloc(repl->entries_size)) )
+ 		print_memory();
+-	if (repl.nentries) {
+-		if (!(repl.counters = (struct ebt_counter *)
+-		   malloc(repl.nentries * sizeof(struct ebt_counter))) )
++	if (repl->nentries) {
++		if (!(repl->counters = (struct ebt_counter *)
++		   malloc(repl->nentries * sizeof(struct ebt_counter))) )
+ 			print_memory();
+ 	}
+ 	else
+-		repl.counters = NULL;
++		repl->counters = NULL;
+ 
+ 	// we want to receive the counters
+-	repl.num_counters = repl.nentries;
+-	optlen += repl.entries_size + repl.num_counters *
++	repl->num_counters = repl->nentries;
++	optlen += repl->entries_size + repl->num_counters *
+ 	   sizeof(struct ebt_counter);
+-	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
++	if (command == 7 || command == 11)
++		optname = EBT_SO_GET_INIT_ENTRIES;
++	else
++		optname = EBT_SO_GET_ENTRIES;
++	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+ 		print_bug("hmm, what is wrong??? bug#1");
++
++	return 0;
++}
++
++// talk with kernel to receive the kernel's table
++int get_table(struct ebt_u_replace *u_repl)
++{
++	int i, j, k, hook;
++	struct ebt_replace repl;
++	struct ebt_u_entry **u_e;
++
++	strcpy(repl.name, u_repl->name);
++	if (u_repl->filename != NULL)
++		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
++	else
++		if (retrieve_from_kernel(&repl, u_repl->command) == -1)
++			return -1;
+ 
+ 	// translate the struct ebt_replace to a struct ebt_u_replace
+ 	memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
+--- ebtables-v2.0pre9/ChangeLog	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre10.001/ChangeLog	Sun Jul 14 21:30:18 2002
+@@ -1,3 +1,8 @@
++20020714
++	* added --atomic options
++20020710
++	* some unlogged changes (due to lazyness)
++	* added --Lc, --Ln, --Lx
+ 20020625
+ 	* user defined chains support: added -N, -X, -E options.
+ 20020621
+--- ebtables-v2.0pre9/ebtables.8	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre10.001/ebtables.8	Mon Jul 15 21:45:55 2002
+@@ -37,6 +37,14 @@
+ .br
+ .BR "ebtables -[b] [" "y/n" "]"
+ .br
++.BR "ebtables --init-table"
++.br
++.BR "ebtables --atomic-init " file
++.br
++.BR "ebtables --atomic-save " file
++.br
++.BR "ebtables --atomic-commit " file
++.br
+ .SH DESCRIPTION
+ .B ebtables
+ is used to set up, maintain, and inspect the tables of Ethernet frame
+@@ -148,11 +156,34 @@
+ database is independent from the rest of
+ .B ebtables
+ and is in a different kernel module.
++.br
++The following three options change the output when not listing the
++database:
++.br
++.B "--Ln"
++.br
++Puts rule numbers in front of every rule.
++.br
++.B "--Lc"
++.br
++Puts the counter value at the end of every rule.
++.br
++.B "--Lx"
++.br
++The output is directly usable as executable commands in a script, to be
++run f.e. at bootup. This option is incompatible with the previous two
++options. When no chain name was specified for the
++.B "-L"
++command, all necessary commands for making the user defined chains and
++renaming the standard chains will be made.
+ .TP
+ .B "-F, --flush"
+ Flush the selected chain. If no chain is selected, every chain will be
+ flushed. This does not change the policy of the chain.
+ .TP
++.B "--init-table"
++Replace the current table data by the initial table data.
++.TP
+ .B "-Z, --zero"
+ Put the counters of the selected chain on zero. If no chain is selected, all the counters
+ are put on zero. This can be used in conjunction with the -L command (see above). 
+@@ -178,6 +209,30 @@
+ structure of the table. It is also allowed to rename a base chain, f.e.
+ if you like PREBRIDGING more than PREROUTING. Be sure to talk about the
+ standard chain names when you would ask a question on a mailing list.
++.TP
++.B "--atomic-init"
++Copy the kernel's initial data of the table to the specified
++file. This can be used as the first action, after which rules are added
++to the file.
++.TP
++.B "--atomic-save"
++Copy the kernel's current data of the table to the specified
++file. This can be used as the first action, after which rules are added
++to the file.
++.TP
++.B "--atomic-commit"
++Replace the kernel table data with the data contained in the specified
++file. This is a useful command that allows you to put all your rules of a
++certain table into the kernel at once, saving the kernel a lot of precious
++time. The file which contains the table data is constructed by using
++either the
++.B "--atomic-init"
++or the
++.B "--atomic-save"
++command to get a starting file. After that, using the
++.B "--atomic"
++option when constructing rules allows you to extend the file and build up
++the complete wanted table.
+ .SS
+ PARAMETERS
+ The following parameters make up a rule specification (as used in the add
+@@ -280,8 +335,8 @@
+ .B ebtables
+ will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
+ .TP
+-.BR "-b --db " "[\fIy/n\fP]"
+-.IR "" "Enable (" y ") or disable (" n ") the database."
++.BR "-b --db [" "y/n" "]"
++Enable (y) or disable (n) the database.
+ .TP
+ .BR "-j, --jump " "\fItarget\fP"
+ The target of the rule. This is one of the following values:
+@@ -291,9 +346,15 @@
+ or a target extension, see
+ .BR "TARGET EXTENSIONS" .
+ .TP
+-.BR "-M, --modprobe " "\fIcommand\fP"
+-When talking to the kernel, use this
+-.IR command " to try to automatically load missing kernel modules."
++.BR "--atomic " file
++Let the command operate on the specified file. The data of the table to
++operate on will be extracted from the file and the result of the operation
++will be saved back into the file. If specified, this option should come
++before the command specification.
++.TP
++.BR "-M, --modprobe " "program"
++When talking to the kernel, use this program to try to automatically load
++missing kernel modules.
+ .SH MATCH EXTENSIONS
+ .B ebtables
+ extensions are precompiled into the userspace tool. So there is no need
+--- ebtables-v2.0pre9/include/ebtables_u.h	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre10.001/include/ebtables_u.h	Sat Jul 13 21:13:46 2002
+@@ -66,6 +66,8 @@
+ 	char command;
+ 	// here we stick the hook to do our thing on (can be -1 if unspecified)
+ 	int selected_hook;
++	// used for the atomic option
++	char *filename;
+ };
+ 
+ struct ebt_u_table
+@@ -191,6 +193,7 @@
+ struct ebt_u_target *find_target(const char *name);
+ struct ebt_u_match *find_match(const char *name);
+ struct ebt_u_watcher *find_watcher(const char *name);
++struct ebt_u_table *find_table(char *name);
+ void deliver_counters(struct ebt_u_replace *repl,
+    unsigned short * counterchanges);
+ void deliver_table(struct ebt_u_replace *repl);
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre2.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre2.001.diff
new file mode 100644
index 0000000..80cc94b
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre2.001.diff
@@ -0,0 +1,121 @@
+Changed the Makefile hacking to initialize things (taken from iptables)
+to using __attribute__ ((constructor)). Makes things cleaner.
+
+--- ebtables-v2.0pre1/Makefile	Wed Apr  3 18:24:15 2002
++++ ebtables-v2.0pre2/Makefile	Sat Apr  6 21:53:00 2002
+@@ -26,7 +26,7 @@
+ 	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \
+ 	-DPROGNAME=\"$(PROGNAME)\" -c -o $@ $<
+ 
+-ebtables: ebtables.o communication.o initext.o $(EXT_OBJS)
++ebtables: ebtables.o communication.o $(EXT_OBJS)
+ 	$(CC) $(CFLAGS) -o $@ $^
+ 
+ $(MANDIR)/man8/ebtables.8: ebtables.8
+--- ebtables-v2.0pre1/ebtables.c	Wed Apr  3 20:06:18 2002
++++ ebtables-v2.0pre2/ebtables.c	Sat Apr  6 21:57:05 2002
+@@ -1051,7 +1051,6 @@
+ 	replace.selected_hook = -1;
+ 	replace.command = 'h';
+ 	// execute the _init functions of the extensions
+-	init_extensions();
+ 
+ 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ 	if (!new_entry)
+--- ebtables-v2.0pre1/extensions/ebt_nat.c	Wed Apr  3 12:27:59 2002
++++ ebtables-v2.0pre2/extensions/ebt_nat.c	Sat Apr  6 21:59:43 2002
+@@ -160,7 +160,8 @@
+ 	opts_d,
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_target(&snat_target);
+ 	register_target(&dnat_target);
+--- ebtables-v2.0pre1/extensions/ebt_ip.c	Wed Apr  3 12:28:44 2002
++++ ebtables-v2.0pre2/extensions/ebt_ip.c	Sat Apr  6 21:58:23 2002
+@@ -301,7 +301,8 @@
+ 	opts,
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute((constructor));
++static void _init(void)
+ {
+ 	register_match(&ip_match);
+ }
+--- ebtables-v2.0pre1/extensions/ebt_arp.c	Wed Apr  3 12:29:17 2002
++++ ebtables-v2.0pre2/extensions/ebt_arp.c	Sat Apr  6 21:58:05 2002
+@@ -271,7 +271,8 @@
+ 	opts,
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_match(&arp_match);
+ }
+--- ebtables-v2.0pre1/extensions/ebt_log.c	Wed Apr  3 16:23:56 2002
++++ ebtables-v2.0pre2/extensions/ebt_log.c	Sat Apr  6 21:59:34 2002
+@@ -182,7 +182,9 @@
+ 	opts,
+ };
+ 
+-void _init(void)
++#undef _init
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_watcher(&log_watcher);
+ }
+--- ebtables-v2.0pre1/extensions/ebt_standard.c	Mon Apr  1 12:49:59 2002
++++ ebtables-v2.0pre2/extensions/ebt_standard.c	Sat Apr  6 22:01:29 2002
+@@ -59,7 +59,8 @@
+ 	opts
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_target(&standard);
+ }
+--- ebtables-v2.0pre1/extensions/ebtable_filter.c	Mon Apr  1 21:25:57 2002
++++ ebtables-v2.0pre2/extensions/ebtable_filter.c	Sat Apr  6 22:00:02 2002
+@@ -24,7 +24,8 @@
+ 	NULL
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_table(&table);
+ }
+--- ebtables-v2.0pre1/extensions/ebtable_nat.c	Wed Apr  3 10:16:46 2002
++++ ebtables-v2.0pre2/extensions/ebtable_nat.c	Sat Apr  6 21:59:53 2002
+@@ -24,7 +24,8 @@
+ 	NULL
+ };
+ 
+-void _init(void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+ 	register_table(&table);
+ }
+--- ebtables-v2.0pre1/include/ebtables_u.h	Wed Apr  3 17:20:17 2002
++++ ebtables-v2.0pre2/include/ebtables_u.h	Sat Apr  6 21:56:16 2002
+@@ -25,9 +25,6 @@
+ #define EBTABLES_U_H
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/br_db.h>
+-#ifdef _INIT
+-#define _init _INIT
+-#endif
+ 
+ struct ebt_u_entries
+ {
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre2.002.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre2.002.diff
new file mode 100644
index 0000000..c638367
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre2.002.diff
@@ -0,0 +1,2204 @@
+This is a big patch.
+Hope I didn't break anything.
+
+--- ebtables-v2.0pre2.001/Makefile	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/Makefile	Thu Apr 11 18:38:47 2002
+@@ -2,8 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre1 (April 2002)"
+-
++PROGVERSION:="2.0pre2.001 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre2.001/ebtables.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/ebtables.c	Wed Apr 10 22:46:27 2002
+@@ -34,7 +34,8 @@
+ #include <asm/types.h>
+ #include "include/ebtables_u.h"
+ 
+-// here are the number-name correspondences kept for the ethernet frame type field
++// here are the number-name correspondences kept for the ethernet
++// frame type field
+ #define PROTOCOLFILE "/etc/etherproto"
+ 
+ #define DATABASEHOOKNR NF_BR_NUMHOOKS
+@@ -81,27 +82,28 @@
+ 
+ // yup, all the possible target names
+ char* standard_targets[NUM_STANDARD_TARGETS] = {
+-	"ACCEPT"  ,
++	"ACCEPT",
+ 	"DROP",
+ 	"CONTINUE",
+ };
+ 
+ // tells what happened to the old rules
+-unsigned short *counterchanges;
++static unsigned short *counterchanges;
+ // holds all the data
+-struct ebt_u_replace replace;
++static struct ebt_u_replace replace;
+ 
+ // the chosen table
+-struct ebt_u_table *table = NULL;
++static struct ebt_u_table *table = NULL;
+ // the lists of supported tables, matches, watchers and targets
+-struct ebt_u_table *tables = NULL;
+-struct ebt_u_match *matches = NULL;
+-struct ebt_u_watcher *watchers = NULL;
+-struct ebt_u_target *targets = NULL;
++static struct ebt_u_table *tables = NULL;
++static struct ebt_u_match *matches = NULL;
++static struct ebt_u_watcher *watchers = NULL;
++static struct ebt_u_target *targets = NULL;
+ 
+ struct ebt_u_target *find_target(const char *name)
+ {
+ 	struct ebt_u_target *t = targets;
++
+ 	while(t && strcmp(t->name, name))
+ 		t = t->next;
+ 	return t;
+@@ -110,6 +112,7 @@
+ struct ebt_u_match *find_match(const char *name)
+ {
+ 	struct ebt_u_match *m = matches;
++
+ 	while(m && strcmp(m->name, name))
+ 		m = m->next;
+ 	return m;
+@@ -118,6 +121,7 @@
+ struct ebt_u_watcher *find_watcher(const char *name)
+ {
+ 	struct ebt_u_watcher *w = watchers;
++
+ 	while(w && strcmp(w->name, name))
+ 		w = w->next;
+ 	return w;
+@@ -126,17 +130,18 @@
+ struct ebt_u_table *find_table(char *name)
+ {
+ 	struct ebt_u_table *t = tables;
++
+ 	while (t && strcmp(t->name, name))
+ 		t = t->next;
+ 	return t;
+ }
+ 
+-// the pointers in here are special:
+-// the struct ebt_target * pointer is actually a struct ebt_u_target * pointer
+-// instead of making yet a few other structs, we just do a cast
+-// we need a struct ebt_u_target pointer because we know the address of the data they
+-// point to won't change. We want to allow that the struct ebt_u_target.t member can
+-// change.
++// The pointers in here are special:
++// The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
++// instead of making yet a few other structs, we just do a cast.
++// We need a struct ebt_u_target pointer because we know the address of the data
++// they point to won't change. We want to allow that the struct ebt_u_target.t
++// member can change.
+ // Same holds for the struct ebt_match and struct ebt_watcher pointers
+ struct ebt_u_entry *new_entry;
+ 
+@@ -149,13 +154,14 @@
+ 	strcpy(e->out, "");
+ 	e->m_list = NULL;
+ 	e->w_list = NULL;
+-	// the init function of the standard target should have put the verdict on CONTINUE
++	// the init function of the standard target should have put the verdict
++	// on CONTINUE
+ 	e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+ 	if (!e->t)
+ 		print_bug("Couldn't load standard target\n");
+ }
+ 
+-// this doesn't free e, basically becoz it's lazy
++// this doesn't free e, becoz the calling function might need e->next
+ void free_u_entry(struct ebt_u_entry *e)
+ {
+ 	struct ebt_u_match_list *m_l, *m_l2;
+@@ -178,6 +184,40 @@
+ 	free(e->t);
+ }
+ 
++// the user will use the match, so put it in new_entry
++static void add_match(struct ebt_u_match *m)
++{
++	struct ebt_u_match_list **m_list, *new;
++
++	m->used = 1;
++	for (m_list = &new_entry->m_list;
++	*m_list; m_list = &(*m_list)->next);
++	new = (struct ebt_u_match_list *)
++	   malloc(sizeof(struct ebt_u_match_list));
++	if (!new)
++		print_memory();
++	*m_list = new;
++	new->next = NULL;
++	new->m = (struct ebt_entry_match *)m;
++}
++
++static void add_watcher(struct ebt_u_watcher *w)
++{
++	struct ebt_u_watcher_list **w_list;
++	struct ebt_u_watcher_list *new;
++
++	w->used = 1;
++	for (w_list = &new_entry->w_list;
++	   *w_list; w_list = &(*w_list)->next);
++	new = (struct ebt_u_watcher_list *)
++	   malloc(sizeof(struct ebt_u_watcher_list));
++	if (!new)
++		print_memory();
++	*w_list = new;
++	new->next = NULL;
++	new->w = (struct ebt_entry_watcher *)w;
++}
++
+ static int global_option_offset = 0;
+ #define OPTION_OFFSET 256
+ static struct option *
+@@ -196,6 +236,8 @@
+ 	*options_offset = global_option_offset;
+ 
+ 	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
++	if (!merge)
++		print_memory();
+ 	memcpy(merge, oldopts, num_old * sizeof(struct option));
+ 	for (i = 0; i < num_new; i++) {
+ 		merge[num_old + i] = newopts[i];
+@@ -219,7 +261,8 @@
+ 		print_memory();
+ 	strcpy(m->m->u.name, m->name);
+ 	m->m->match_size = size;
+-	ebt_options = merge_options(ebt_options, m->extra_ops, &(m->option_offset));
++	ebt_options = merge_options
++	   (ebt_options, m->extra_ops, &(m->option_offset));
+ 	m->init(m->m);
+ 
+ 	for (i = &matches; *i; i = &((*i)->next));
+@@ -237,7 +280,8 @@
+ 		print_memory();
+ 	strcpy(w->w->u.name, w->name);
+ 	w->w->watcher_size = size;
+-	ebt_options = merge_options(ebt_options, w->extra_ops, &(w->option_offset));
++	ebt_options = merge_options
++	   (ebt_options, w->extra_ops, &(w->option_offset));
+ 	w->init(w->w);
+ 
+ 	for (i = &watchers; *i; i = &((*i)->next));
+@@ -255,7 +299,8 @@
+ 		print_memory();
+ 	strcpy(t->t->u.name, t->name);
+ 	t->t->target_size = size;
+-	ebt_options = merge_options(ebt_options, t->extra_ops, &(t->option_offset));
++	ebt_options = merge_options
++	   (ebt_options, t->extra_ops, &(t->option_offset));
+ 	t->init(t->t);
+ 	for (i = &targets; *i; i = &((*i)->next));
+ 	t->next = NULL;
+@@ -292,7 +337,7 @@
+ 	return 0;
+ }
+ 
+-/* helper function: processes a line of data from the file brebt_protocolnames */
++// helper function: processes a line of data from the file /etc/etherproto
+ int get_a_line(char *buffer, char *value, FILE *ifp)
+ {
+ 	int i, hlp;
+@@ -314,7 +359,7 @@
+ 
+ 	// buffer[0] already contains the first letter
+ 	for (i = 1; i < 21; i++) {
+-		hlp = fscanf(ifp, "%c", buffer+i);
++		hlp = fscanf(ifp, "%c", buffer + i);
+ 		if (hlp == EOF || hlp == 0) return -1;
+ 		if (buffer[i] == '\t' || buffer[i] == ' ')
+ 			break;
+@@ -327,7 +372,8 @@
+ 	// buffer[0] already contains the first letter
+ 	for (i = 1; i < 5; i++) {
+ 		hlp = fscanf(ifp, "%c", value+i);
+-		if (value[i] == '\n' || value[i] == '\t' || value[i] == ' ' || hlp == EOF)
++		if (value[i] == '\n' || value[i] == '\t' ||
++		   value[i] == ' ' || hlp == EOF)
+ 			break;
+ 	}
+ 	if (i == 5) return -1;
+@@ -342,7 +388,7 @@
+ 	return 0;
+ }
+ 
+-/* helper function for list_em() */
++// helper function for list_em()
+ int number_to_name(unsigned short proto, char *name)
+ {
+ 	FILE *ifp;
+@@ -363,13 +409,12 @@
+ 		fclose(ifp);
+ 		return 0;
+ 	}
+-	return -1;
+ }
+ 
+-/* helper function for list_rules() */
++// helper function for list_rules()
+ static void list_em(int hooknr)
+ {
+-	int i, space = 0;
++	int i, j, space = 0, digits;
+ 	struct ebt_u_entry *hlp;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+@@ -379,7 +424,8 @@
+ 	char name[21];
+ 
+ 	hlp = replace.hook_entry[hooknr]->entries;
+-	printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr], standard_targets[(int)(replace.hook_entry[hooknr]->policy)]);
++	printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
++	   standard_targets[replace.hook_entry[hooknr]->policy]);
+ 	printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
+ 
+ 	i = replace.hook_entry[hooknr]->nentries;
+@@ -389,19 +435,18 @@
+ 	}
+ 
+ 	for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
+-		int j = i + 1, space2 = 0;
+-		// a little work to get nice rule numbers
+-		// this can probably be done easier - so what
++		digits = 0;
++		// A little work to get nice rule numbers.
+ 		while (j > 9) {
+-			space2++;
++			digits++;
+ 			j /= 10;
+ 		}
+-		for (j = 0; j < space - space2; j++)
++		for (j = 0; j < space - digits; j++)
+ 			printf(" ");
+ 		printf("%d. ", i + 1);
+ 
+-		// don't print anything about the protocol if no protocol was specified
+-		// obviously this means any protocol will do
++		// Don't print anything about the protocol if no protocol was
++		// specified, obviously this means any protocol will do.
+ 		if (!(hlp->bitmask & EBT_NOPROTO)) {
+ 			printf("eth proto: ");
+ 			if (hlp->invflags & EBT_IPROTO)
+@@ -416,20 +461,20 @@
+ 			}
+ 		}
+ 		if (hlp->bitmask & EBT_SOURCEMAC) {
+-			int j;
+ 			printf("source mac: ");
+ 			if (hlp->invflags & EBT_ISOURCE)
+ 				printf("! ");
+ 			for (j = 0; j < ETH_ALEN; j++)
+-				printf("%02x%s", hlp->sourcemac[j], (j == ETH_ALEN - 1) ? ", " : ":");
++				printf("%02x%s", hlp->sourcemac[j],
++				   (j == ETH_ALEN - 1) ? ", " : ":");
+ 		}
+ 		if (hlp->bitmask & EBT_DESTMAC) {
+-			int j;
+ 			printf("dest mac: ");
+ 			if (hlp->invflags & EBT_IDEST)
+ 				printf("! ");
+ 			for (j = 0; j < ETH_ALEN; j++)
+-				printf("%02x%s", hlp->destmac[j], (j == ETH_ALEN - 1) ? ", " : ":");
++				printf("%02x%s", hlp->destmac[j],
++				   (j == ETH_ALEN - 1) ? ", " : ":");
+ 		}
+ 		if (hlp->in[0] != '\0') {
+ 			if (hlp->invflags & EBT_IIN)
+@@ -462,9 +507,10 @@
+ 		printf("target: ");
+ 		t = find_target(hlp->t->u.name);
+ 		if (!t)
+-			print_error("Target not found.");
++			print_bug("Target not found");
+ 		t->print(hlp, hlp->t);
+-		printf(", count = %llu", replace.counters[replace.counter_entry[hooknr] + i].pcnt);
++		printf(", count = %llu",
++		   replace.counters[replace.counter_entry[hooknr] + i].pcnt);
+ 		printf("\n");
+ 		hlp = hlp->next;
+ 	}
+@@ -492,30 +538,30 @@
+ 	struct ebt_u_watcher_list *w_l;
+ 
+ 	printf(
+-	"%s v%s\n"
+-	"Usage:\n"
+-	"ebtables -[ADI] chain rule-specification [options]\n"
+-	"ebtables -P chain target\n"
+-	"ebtables -[LFZ] [chain]\n"
+-	"ebtables -[b] [y,n]\n"
+-	"Commands:\n"
+-	"--append -A chain             : Append to chain\n"
+-	"--delete -D chain             : Delete matching rule from chain\n"
+-	"--delete -D chain rulenum     : Delete rule at position rulenum from chain\n"
+-	"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+-	"--list   -L [chain]           : List the rules in a chain or in all chains\n"
+-	"--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
+-	"--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
+-	"--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
+-	"--policy -P chain target      : Change policy on chain to target\n"
+-	"Options:\n"
+-	"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+-	"--src    -s [!] address       : source mac address\n"
+-	"--dst    -d [!] address       : destination mac address\n"
+-	"--in-if  -i [!] name          : network input interface name\n"
+-	"--out-if -o [!] name          : network output interface name\n"
+-	"--version -V                  : print package version\n"
+-	"\n" ,
++"%s v%s\n"
++"Usage:\n"
++"ebtables -[ADI] chain rule-specification [options]\n"
++"ebtables -P chain target\n"
++"ebtables -[LFZ] [chain]\n"
++"ebtables -[b] [y,n]\n"
++"Commands:\n"
++"--append -A chain             : Append to chain\n"
++"--delete -D chain             : Delete matching rule from chain\n"
++"--delete -D chain rulenum     : Delete rule at position rulenum from chain\n"
++"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
++"--list   -L [chain]           : List the rules in a chain or in all chains\n"
++"--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
++"--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
++"--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
++"--policy -P chain target      : Change policy on chain to target\n"
++"Options:\n"
++"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
++"--src    -s [!] address       : source mac address\n"
++"--dst    -d [!] address       : destination mac address\n"
++"--in-if  -i [!] name          : network input interface name\n"
++"--out-if -o [!] name          : network output interface name\n"
++"--version -V                  : print package version\n"
++"\n" ,
+ 	prog_name,
+ 	prog_version);
+ 
+@@ -538,7 +584,7 @@
+ 	exit(0);
+ }
+ 
+-/* execute command L */
++// execute command L
+ static void list_rules()
+ {
+ 	int i;
+@@ -563,7 +609,8 @@
+ 		replace.num_counters = replace.nentries;
+ 		if (replace.nentries) {
+ 			// '+ 1' for the CNT_END
+-			if ( !(counterchanges = (unsigned short *)malloc((replace.nentries + 1) * sizeof(unsigned short))) )
++			if (!(counterchanges = (unsigned short *) malloc(
++			   (replace.nentries + 1) * sizeof(unsigned short))))
+ 				print_memory();
+ 			// done nothing special to the rules
+ 			for (i = 0; i < replace.nentries; i++)
+@@ -611,12 +658,14 @@
+ 	if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+ 		exit(0);
+ 	oldnentries = replace.nentries;
+-	replace.nentries = replace.nentries - replace.hook_entry[replace.selected_hook]->nentries;
++	replace.nentries = replace.nentries -
++	   replace.hook_entry[replace.selected_hook]->nentries;
+ 
+ 	// delete the counters belonging to the specified chain
+ 	if (replace.nentries) {
+ 		// +1 for CNT_END
+-		if ( !(counterchanges = (unsigned short *)malloc((oldnentries + 1) * sizeof(unsigned short))) )
++		if ( !(counterchanges = (unsigned short *)
++		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+ 		cnt = counterchanges;
+ 		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+@@ -660,23 +709,31 @@
+ 
+ 	// handle '-D chain rulenr' command
+ 	if (rule_nr != -1) {
+-		if (rule_nr > replace.hook_entry[replace.selected_hook]->nentries)
++		if (rule_nr >
++		   replace.hook_entry[replace.selected_hook]->nentries)
+ 			return 0;
++		// user starts counting from 1
+ 		return rule_nr - 1;
+ 	}
+ 	u_e = replace.hook_entry[replace.selected_hook]->entries;
+-	// check for an existing rule (if there are duplicate rules, take the first occurance)
+-	for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries; i++, u_e = u_e->next) {
++	// check for an existing rule (if there are duplicate rules,
++	// take the first occurance)
++	for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
++	   i++, u_e = u_e->next) {
+ 		if (!u_e)
+ 			print_bug("Hmm, trouble");
+ 		if ( u_e->ethproto == new_entry->ethproto
+-		     && !strncmp(u_e->in, new_entry->in, IFNAMSIZ)
+-		     && !strncmp(u_e->out, new_entry->out, IFNAMSIZ) && u_e->bitmask == new_entry->bitmask) {
+-			if (new_entry->bitmask & EBT_SOURCEMAC && strncmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
++		   && !strcmp(u_e->in, new_entry->in)
++		   && !strcmp(u_e->out, new_entry->out)
++		   && u_e->bitmask == new_entry->bitmask) {
++			if (new_entry->bitmask & EBT_SOURCEMAC &&
++			   strcmp(u_e->sourcemac, new_entry->sourcemac))
+ 				continue;
+-			if (new_entry->bitmask & EBT_DESTMAC && strncmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
++			if (new_entry->bitmask & EBT_DESTMAC &&
++			   strcmp(u_e->destmac, new_entry->destmac))
+ 				continue;
+-			if (new_entry->bitmask != u_e->bitmask || new_entry->invflags != u_e->invflags)
++			if (new_entry->bitmask != u_e->bitmask ||
++			   new_entry->invflags != u_e->invflags)
+ 				continue;
+ 			// compare all matches
+ 			m_l = new_entry->m_list;
+@@ -684,7 +741,8 @@
+ 			while (m_l) {
+ 				m = (struct ebt_u_match *)(m_l->m);
+ 				m_l2 = u_e->m_list;
+-				while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
++				while (m_l2 &&
++				   strcmp(m_l2->m->u.name, m->m->u.name))
+ 					m_l2 = m_l2->next;
+ 				if (!m_l2 || !m->compare(m->m, m_l2->m))
+ 					goto letscontinue;
+@@ -707,7 +765,8 @@
+ 			while (w_l) {
+ 				w = (struct ebt_u_watcher *)(w_l->w);
+ 				w_l2 = u_e->w_list;
+-				while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
++				while (w_l2 &&
++				   strcmp(w_l2->w->u.name, w->w->u.name))
+ 					w_l2 = w_l2->next;
+ 				if (!w_l2 || !w->compare(w->w, w_l2->w))
+ 					goto letscontinue;
+@@ -743,8 +802,10 @@
+ 	struct ebt_u_watcher_list *w_l;
+ 
+ 	if (rule_nr != -1) { // command -I
+-		if (--rule_nr > replace.hook_entry[replace.selected_hook]->nentries)
+-			print_error("rule nr too high: %d > %d.", rule_nr, replace.hook_entry[replace.selected_hook]->nentries);
++		if (--rule_nr >
++		   replace.hook_entry[replace.selected_hook]->nentries)
++			print_error("rule nr too high: %d > %d", rule_nr,
++			   replace.hook_entry[replace.selected_hook]->nentries);
+ 	} else
+ 		rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
+ 	// we're adding one rule
+@@ -754,7 +815,8 @@
+ 
+ 	// handle counter stuff
+ 	// +1 for CNT_END
+-	if ( !(counterchanges = (unsigned short *)malloc((replace.nentries + 1) * sizeof(unsigned short))) )
++	if ( !(counterchanges = (unsigned short *)
++	   malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+ 		print_memory();
+ 	cnt = counterchanges;
+ 	for (i = 0; i < replace.selected_hook; i++) {
+@@ -813,7 +875,7 @@
+ 	struct ebt_u_entry *u_e, *u_e2;
+ 
+ 	if ( (i = check_rule_exists(rule_nr)) == -1 )
+-		print_error("Sorry, rule does not exists.");
++		print_error("Sorry, rule does not exists");
+ 
+ 	// we're deleting a rule
+ 	replace.num_counters = replace.nentries;
+@@ -827,7 +889,8 @@
+ 		}
+ 		lentmp += i;
+ 		// +1 for CNT_END
+-		if ( !(counterchanges = (unsigned short *)malloc((replace.num_counters + 1) * sizeof(unsigned short))) )
++		if ( !(counterchanges = (unsigned short *)malloc(
++		   (replace.num_counters + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+ 		cnt = counterchanges;
+ 		for (j = 0; j < lentmp; j++) {
+@@ -871,8 +934,8 @@
+ 
+ 	if (zerochain == -1) {
+ 		// tell main() we don't update the counters
+-		// this results in tricking the kernel to zero his counters, naively expecting
+-		// userspace to update its counters. Muahahaha
++		// this results in tricking the kernel to zero his counters,
++		// naively expecting userspace to update its counters. Muahahaha
+ 		counterchanges = NULL;
+ 		replace.num_counters = 0;
+ 	} else {
+@@ -881,7 +944,10 @@
+ 
+ 		if (replace.hook_entry[zerochain]->nentries == 0)
+ 			exit(0);
+-		counterchanges = (unsigned short *)malloc((replace.nentries + 1) * sizeof(unsigned short));
++		counterchanges = (unsigned short *)
++		   malloc((replace.nentries + 1) * sizeof(unsigned short));
++		if (!counterchanges)
++			print_memory();
+ 		cnt = counterchanges;
+ 		for (i = 0; i < zerochain; i++) {
+ 			if (!(replace.valid_hooks & (1 << i)))
+@@ -915,10 +981,12 @@
+ 
+ 	// 0 : database disabled (-db n)
+ 	if (!(nr.nentries))
+-		print_error("Database not present (disabled), try ebtables --db y.");
+-	(nr.nentries)--;
+-	if (!nr.nentries) print_error("Database empty.");
+-	if ( !(db = (struct brdb_dbentry *) malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
++		print_error("Database not present"
++		            " (disabled), try ebtables --db y");
++	nr.nentries--;
++	if (!nr.nentries) print_error("Database empty");
++	if ( !(db = (struct brdb_dbentry *)
++	   malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
+ 		print_memory();
+ 
+ 	get_db(nr.nentries, db);
+@@ -931,7 +999,7 @@
+ 		"out-if  : %s\n"
+ 		"protocol: ", i + 1, hooknames[db->hook], db->in, db->out);
+ 		if (db->ethproto == IDENTIFY802_3)
+-			printf("NO PROTO, OLD 802.3 STYLE LENGTH FIELD\n");
++			printf("802.2/802.3 STYLE LENGTH FIELD\n");
+ 		else {
+ 			if (number_to_name(ntohs(db->ethproto), name))
+ 				printf("%x\n",ntohs(db->ethproto));
+@@ -943,13 +1011,13 @@
+ 	exit(0);
+ }
+ 
+-// handle counter and db disabling and enabling
++// handle db [dis,en]abling
+ static void allowdb(char yorn)
+ {
+ 	__u16 decision;
+ 
+ 	if (yorn != 'y' && yorn != 'n')
+-		print_error("Option [y] or [n] needed.");
++		print_error("Option [y] or [n] needed");
+ 
+ 	if (yorn == 'y')
+ 		decision = BRDB_DB;
+@@ -980,7 +1048,8 @@
+ 		if (strcasecmp(buffer, name))
+ 			continue;
+ 		i = (unsigned short) strtol(value, &bfr, 16);
+-		if (*bfr != '\0') return -1;
++		if (*bfr != '\0')
++			return -1;
+ 		new_entry->ethproto = i;
+ 		fclose(ifp);
+ 		return 0;
+@@ -1022,7 +1091,7 @@
+ void check_option(unsigned int *flags, unsigned int mask)
+ {
+ 	if (*flags & mask)
+-		print_error("Multiple use of same option not allowed.");
++		print_error("Multiple use of same option not allowed");
+ 	*flags |= mask;
+ }
+ 
+@@ -1040,17 +1109,21 @@
+ {
+ 	char *buffer, allowbc = 'n';
+ 	int c, i;
+-	int zerochain = -1; // this special one for the -Z option (we can have -Z <this> -L <that>)
++	// this special one for the -Z option (we can have -Z <this> -L <that>)
++	int zerochain = -1;
+ 	int policy = -1;
+ 	int rule_nr = -1;// used for -D chain number
+ 	struct ebt_u_target *t;
++	struct ebt_u_match *m;
++	struct ebt_u_watcher *w;
++	struct ebt_u_match_list *m_l;
++	struct ebt_u_watcher_list *w_l;
+ 
+-	// initialize the table name, OPT_ flags and selected hook
++	// initialize the table name, OPT_ flags, selected hook and command
+ 	strcpy(replace.name, "filter");
+ 	replace.flags = 0;
+ 	replace.selected_hook = -1;
+ 	replace.command = 'h';
+-	// execute the _init functions of the extensions
+ 
+ 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ 	if (!new_entry)
+@@ -1059,7 +1132,8 @@
+ 	initialize_entry(new_entry);
+ 
+ 	// getopt saves the day
+-	while ((c = getopt_long(argc, argv, "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
++	while ((c = getopt_long(argc, argv,
++	   "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
+ 		switch (c) {
+ 
+ 		case 'A': // add a rule
+@@ -1068,33 +1142,39 @@
+ 		case 'I': // insert a rule
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+-				print_error("Multiple commands not allowed.");
++				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
+ 			if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+-				print_error("Bad chain.");
+-			// '-' denotes another option, if no other option it must be the (optional) rule number
+-			if (c == 'D' && optind < argc && argv[optind][0] != '-') {
++				print_error("Bad chain");
++			if (c == 'D' && optind < argc &&
++			   argv[optind][0] != '-') {
+ 				rule_nr = strtol(argv[optind], &buffer, 10);
+ 				if (*buffer != '\0' || rule_nr < 0)
+-					print_error("Problem with the specified rule number.");
++					print_error("Problem with the "
++					            "specified rule number");
+ 				optind++;
+ 			}
+ 			if (c == 'P') {
+ 				if (optind >= argc)
+-					print_error("No policy specified.");
++					print_error("No policy specified");
+ 				for (i = 0; i < 2; i++)
+-					if (!strcmp(argv[optind], standard_targets[i]))
++					if (!strcmp(argv[optind],
++					   standard_targets[i])) {
+ 						policy = i;
++						break;
++					}
+ 				if (policy == -1)
+-					print_error("Wrong policy.");
++					print_error("Wrong policy");
+ 				optind++;
+ 			}
+ 			if (c == 'I') {
+ 				if (optind >= argc)
+-					print_error("No rulenr for -I specified.");
++					print_error("No rulenr for -I"
++					            " specified");
+ 				rule_nr = strtol(argv[optind], &buffer, 10);
+ 				if (*buffer != '\0' || rule_nr < 0)
+-					print_error("Problem with the specified rule number.");
++					print_error("Problem with the specified"
++					            " rule number");
+ 				optind++;
+ 			}
+ 			break;
+@@ -1104,24 +1184,29 @@
+ 		case 'Z': // zero counters
+ 			if (c == 'Z') {
+ 				if (replace.flags & OPT_ZERO)
+-					print_error("Multiple commands not allowed.");
+-				if ( (replace.flags & OPT_COMMAND && replace.command != 'L'))
+-					print_error("command -Z only allowed together with command -L.");
++					print_error("Multiple commands"
++					            " not allowed");
++				if ( (replace.flags & OPT_COMMAND &&
++				   replace.command != 'L'))
++					print_error("command -Z only allowed "
++					            "together with command -L");
+ 				replace.flags |= OPT_ZERO;
+ 			} else {
+ 				replace.command = c;
+ 				if (replace.flags & OPT_COMMAND)
+-					print_error("Multiple commands not allowed.");
++					print_error("Multiple commands"
++					            " not allowed");
+ 				replace.flags |= OPT_COMMAND;
+ 			}
+ 			i = -1;
+ 			if (optarg) {
+ 				if ( (i = get_hooknr(optarg)) == -1 )
+-					print_error("Bad chain.");
++					print_error("Bad chain");
+ 			} else
+ 				if (optind < argc && argv[optind][0] != '-') {
+-					if ( (i = get_hooknr(argv[optind])) == -1 )
+-						print_error("Bad chain.");
++					if ((i = get_hooknr(argv[optind]))
++					   == -1)
++						print_error("Bad chain");
+ 					optind++;
+ 				}
+ 			if (i != -1) {
+@@ -1135,47 +1220,34 @@
+ 		case 'V': // version
+ 			replace.command = 'V';
+ 			if (replace.flags & OPT_COMMAND)
+-				print_error("Multiple commands not allowed.");
++				print_error("Multiple commands not allowed");
+ 			printf("%s, %s\n", prog_name, prog_version);
+ 			exit(0);
+ 
+ 		case 'h': // help
+ 			if (replace.flags & OPT_COMMAND)
+-				print_error("Multiple commands not allowed.");
++				print_error("Multiple commands not allowed");
+ 			replace.command = 'h';
+ 			// All other arguments should be extension names
+ 			while (optind < argc) {
+ 				struct ebt_u_match *m;
+ 				struct ebt_u_watcher *w;
+ 
+-				if ((m = find_match(argv[optind]))) {
+-					struct ebt_u_match_list **m_list, *new;
+-
+-					m->used = 1;
+-					for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+-					new = (struct ebt_u_match_list *)malloc(sizeof(struct ebt_u_match_list));
+-					if (!new)
+-						print_memory();
+-					*m_list = new;
+-					new->next = NULL;
+-					new->m = (struct ebt_entry_match *)m;
+-				} else if ((w = find_watcher(argv[optind]))) {
+-					struct ebt_u_watcher_list **w_list, *new;
+-					w->used = 1;
+-					for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+-					new = (struct ebt_u_watcher_list *)malloc(sizeof(struct ebt_u_watcher_list));
+-					if (!new)
+-						print_memory();
+-					*w_list = new;
+-					new->next = NULL;
+-					new->w = (struct ebt_entry_watcher *)w;
+-				} else {
++				if ((m = find_match(argv[optind])))
++					add_match(m);
++				else if ((w = find_watcher(argv[optind])))
++					add_watcher(w);
++				else {
+ 					if (!(t = find_target(argv[optind])))
+-						print_error("Extension %s not found.", argv[optind]);
++						print_error("Extension %s "
++						   "not found", argv[optind]);
+ 					if (replace.flags & OPT_JUMP)
+-						print_error("Sorry, you can only see help for one target extension each time.");
++						print_error("Sorry, you can "
++						 "only see help for one "
++						 "target extension each time");
+ 					replace.flags |= OPT_JUMP;
+-					new_entry->t = (struct ebt_entry_target *)t;
++					new_entry->t =
++					   (struct ebt_entry_target *)t;
+ 				}
+ 				optind++;
+ 			}
+@@ -1184,7 +1256,7 @@
+ 		case 't': // table
+ 			check_option(&replace.flags, OPT_TABLE);
+ 			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
+-				print_error("Table name too long.");
++				print_error("Table name too long");
+ 			strcpy(replace.name, optarg);
+ 			break;
+ 
+@@ -1195,56 +1267,69 @@
+ 		case 's': // source mac
+ 		case 'd': // destination mac
+ 			if ((replace.flags & OPT_COMMAND) == 0)
+-				print_error("No command specified.");
+-			if ( replace.command != 'A' && replace.command != 'D' && replace.command != 'I')
+-				print_error("Command and option do not match.");
++				print_error("No command specified");
++			if ( replace.command != 'A' &&
++			   replace.command != 'D' && replace.command != 'I')
++				print_error("Command and option do not match");
+ 			if (c == 'i') {
+ 				check_option(&replace.flags, OPT_IN);
+-				if (replace.selected_hook == 2)
+-					print_error("Use in-interface only in INPUT, FORWARD and PREROUTING chains.");
++				if (replace.selected_hook > 2)
++					print_error("Use in-interface only in "
++					"INPUT, FORWARD and PREROUTING chains");
+ 				if (check_inverse(optarg))
+ 					new_entry->invflags |= EBT_IIN;
+ 
+ 				if (optind > argc)
+-					print_error("Missing interface argument.");
++					print_error("No in-interface "
++					            "specified");
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interfacelength.");
+-				strncpy(new_entry->in, argv[optind - 1], IFNAMSIZ);
++					print_error("Illegal interfacelength");
++				strcpy(new_entry->in, argv[optind - 1]);
+ 				break;
+ 			}
+ 			if (c == 'o') {
+ 				check_option(&replace.flags, OPT_OUT);
+-				if (replace.selected_hook == 0)
+-					print_error("Use out-interface only in OUTPUT, FORWARD and POSTROUTING chains.");
++				if (replace.selected_hook < 2)
++					print_error("Use out-interface only"
++					   " in OUTPUT, FORWARD and "
++					   "POSTROUTING chains");
+ 				if (check_inverse(optarg))
+ 					new_entry->invflags |= EBT_IOUT;
+ 
+ 				if (optind > argc)
+-					print_error("Missing interface argument.");
++					print_error("No out-interface "
++					            "specified");
++
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interface length.");
+-				strncpy(new_entry->out, argv[optind - 1], IFNAMSIZ);
++					print_error("Illegal interface "
++					            "length");
++				strcpy(new_entry->out, argv[optind - 1]);
+ 				break;
+ 			}
+ 			if (c == 'j') {
+ 
+ 				check_option(&replace.flags, OPT_JUMP);
+ 				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+-					if (!strcmp(optarg, standard_targets[i])) {
+-						t = find_target(EBT_STANDARD_TARGET);
+-						((struct ebt_standard_target *)t->t)->verdict = i;
++					if (!strcmp(optarg,
++					   standard_targets[i])) {
++						t = find_target(
++						   EBT_STANDARD_TARGET);
++						((struct ebt_standard_target *)
++						   t->t)->verdict = i;
+ 						break;
+ 					}
+ 				// must be an extension then
+ 				if (i == NUM_STANDARD_TARGETS) {
+ 					struct ebt_u_target *t;
+ 					t = find_target(optarg);
+-					if (!t)
+-						print_error("Illegal target name.");
+-					new_entry->t = (struct ebt_entry_target *)t;
+-				} else
+-					((struct ebt_standard_target *)(((struct ebt_u_target *)new_entry->t)->t))->verdict = i;
+-
++					// -j standard not allowed either
++					if (!t || t ==
++					   (struct ebt_u_target *)new_entry->t)
++						print_error("Illegal target "
++						            "name");
++					new_entry->t =
++					   (struct ebt_entry_target *)t;
++				}
+ 				break;
+ 			}
+ 			if (c == 's') {
+@@ -1253,9 +1338,12 @@
+ 					new_entry->invflags |= EBT_ISOURCE;
+ 
+ 				if (optind > argc)
+-					print_error("Missing source mac argument.");
+-				if (getmac(argv[optind - 1], new_entry->sourcemac))
+-					print_error("Problem with specified source mac.");
++					print_error("No source mac "
++					            "specified");
++				if (getmac(argv[optind - 1],
++				   new_entry->sourcemac))
++					print_error("Problem with specified "
++					            "source mac");
+ 				new_entry->bitmask |= EBT_SOURCEMAC;
+ 				break;
+ 			}
+@@ -1265,9 +1353,12 @@
+ 					new_entry->invflags |= EBT_IDEST;
+ 
+ 				if (optind > argc)
+-					print_error("Missing destination mac argument.");
+-				if (getmac(argv[optind - 1], new_entry->destmac))
+-					print_error("Problem with specified destination mac.");
++					print_error("No destination mac "
++					            "specified");
++				if (getmac(argv[optind - 1],
++				   new_entry->destmac))
++					print_error("Problem with specified "
++					            "destination mac");
+ 				new_entry->bitmask |= EBT_DESTMAC;
+ 				break;
+ 			}
+@@ -1276,22 +1367,26 @@
+ 				new_entry->invflags |= EBT_IPROTO;
+ 
+ 			if (optind > argc)
+-					print_error("Missing protocol argument.");
++				print_error("No protocol specified");
+ 			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+ 			i = strtol(argv[optind - 1], &buffer, 16);
+ 			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+-				print_error("Problem with the specified protocol.");
++				print_error("Problem with the specified "
++				            "protocol");
+ 			new_entry->ethproto = i;
+ 			if (*buffer != '\0')
+ 				if (name_to_protocol(argv[optind - 1]) == -1)
+-					print_error("Problem with the specified protocol.");
+-			if (new_entry->ethproto < 1536 && !(new_entry->bitmask & EBT_802_3))
+-				print_error("Sorry, protocols have values above or equal to 1536 (0x0600).");
++					print_error("Problem with the specified"
++					            " protocol");
++			if (new_entry->ethproto < 1536 &&
++			   !(new_entry->bitmask & EBT_802_3))
++				print_error("Sorry, protocols have values above"
++				            " or equal to 1536 (0x0600)");
+ 			break;
+ 
+ 		case 'b': // allow database?
+ 			if (replace.flags & OPT_COMMAND)
+-				print_error("Multiple commands not allowed.");
++				print_error("Multiple commands not allowed");
+ 			replace.command = c;
+ 			allowbc = *optarg;
+ 			break;
+@@ -1300,49 +1395,32 @@
+ 
+ 			// is it a target option?
+ 			t = (struct ebt_u_target *)new_entry->t;
+-			if (!(t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {
+-				struct ebt_u_match *m;
+-
+-				// is it a match_option?
+-				for (m = matches; m; m = m->next)
+-					if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
+-						break;
++			if ((t->parse(c - t->option_offset, argv, argc,
++			   new_entry, &t->flags, &t->t)))
++				continue;
+ 
+-				if (m == NULL) {
+-					struct ebt_u_watcher *w;
++			// is it a match_option?
++			for (m = matches; m; m = m->next)
++				if (m->parse(c - m->option_offset, argv,
++				   argc, new_entry, &m->flags, &m->m))
++					break;
+ 
+-					// is it a watcher option?
+-					for (w = watchers; w; w = w->next)
+-						if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w))
+-							break;
+-
+-					if (w == NULL)
+-						print_error("Unknown argument.");
+-					if (w->used == 0) {
+-						struct ebt_u_watcher_list **w_list, *new;
+-						w->used = 1;
+-						for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+-						new = (struct ebt_u_watcher_list *)malloc(sizeof(struct ebt_u_watcher_list));
+-						if (!new)
+-							print_memory();
+-						*w_list = new;
+-						new->next = NULL;
+-						new->w = (struct ebt_entry_watcher *)w;
+-					}
+-				} else {
+-					if (m->used == 0) {
+-						struct ebt_u_match_list **m_list, *new;
+-						m->used = 1;
+-						for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+-						new = (struct ebt_u_match_list *)malloc(sizeof(struct ebt_u_match_list));
+-						if (!new)
+-							print_memory();
+-						*m_list = new;
+-						new->next = NULL;
+-						new->m = (struct ebt_entry_match *)m;
+-					}
+-				}
++			if (m != NULL) {
++				if (m->used == 0)
++					add_match(m);
++				continue;
+ 			}
++
++			// is it a watcher option?
++			for (w = watchers; w; w = w->next)
++				if (w->parse(c-w->option_offset, argv,
++				   argc, new_entry, &w->flags, &w->w))
++					break;
++
++			if (w == NULL)
++				print_error("Unknown argument");
++			if (w->used == 0)
++				add_watcher(w);
+ 		}
+ 	}
+ 
+@@ -1352,43 +1430,40 @@
+ 	if (replace.command == 'L' && replace.selected_hook == DATABASEHOOKNR)
+ 		list_db();
+ 
+-	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' && replace.flags & OPT_ZERO )
+-		print_error("Command -Z only allowed together with command -L.");
++	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
++	   replace.flags & OPT_ZERO )
++		print_error("Command -Z only allowed together with command -L");
+ 
+-	if (replace.command == 'A' || replace.command == 'I' || replace.command == 'D') {
++	if (replace.command == 'A' || replace.command == 'I' ||
++	   replace.command == 'D') {
+ 		if (replace.selected_hook == -1)
+-			print_error("Not enough information.");
++			print_error("Not enough information");
+ 	}
+ 
+ 	if ( !(table = find_table(replace.name)) )
+-		print_error("Bad table name.");
++		print_error("Bad table name");
+ 
+ 	// do this after parsing everything, so we can print specific info
+ 	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ 		print_help();
+ 
+ 	// do the final checks
+-	{
+-	struct ebt_u_match_list *m_l = new_entry->m_list;
+-	struct ebt_u_match *m;
+-	struct ebt_u_watcher_list *w_l = new_entry->w_list;
+-	struct ebt_u_watcher *w;
+-	struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
+-
++	m_l = new_entry->m_list;
++	w_l = new_entry->w_list;
++	t = (struct ebt_u_target *)new_entry->t;
+ 	while (m_l) {
+ 		m = (struct ebt_u_match *)(m_l->m);
+-		m->final_check(new_entry, m->m, replace.name, replace.selected_hook);
++		m->final_check(new_entry, m->m, replace.name,
++		   replace.selected_hook);
+ 		m_l = m_l->next;
+ 	}
+-
+ 	while (w_l) {
+ 		w = (struct ebt_u_watcher *)(w_l->w);
+-		w->final_check(new_entry, w->w, replace.name, replace.selected_hook);
++		w->final_check(new_entry, w->w, replace.name,
++		   replace.selected_hook);
+ 		w_l = w_l->next;
+ 	}
+-
+ 	t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+-	}
+ 	
+ 	// so, the extensions can work with the host endian
+ 	// the kernel does not have to do this ofcourse
+@@ -1396,10 +1471,10 @@
+ 
+ 	// get the kernel's information
+ 	get_table(&replace);
+-	replace.nentries = replace.nentries;
+ 	// check if selected_hook is a valid_hook
+-	if (replace.selected_hook >= 0 && !(replace.valid_hooks & (1 << replace.selected_hook)))
+-		print_error("Bad chain name.");
++	if (replace.selected_hook >= 0 &&
++	   !(replace.valid_hooks & (1 << replace.selected_hook)))
++		print_error("Bad chain name");
+ 	if (replace.command == 'P')
+ 		change_policy(policy);
+ 	else if (replace.command == 'L') {
+--- ebtables-v2.0pre2.001/communication.c	Wed Apr  3 17:22:39 2002
++++ ebtables-v2.0pre2.002/communication.c	Wed Apr 10 22:10:49 2002
+@@ -25,12 +25,6 @@
+ 
+ extern char* hooknames[NF_BR_NUMHOOKS];
+ 
+-void print_memory()
+-{
+-	printf("Out of memory\n");
+-	exit(0);
+-}
+-
+ int sockfd = -1;
+ 
+ void get_sockfd()
+@@ -38,7 +32,7 @@
+ 	if (sockfd == -1) {
+ 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+ 		if (sockfd < 0)
+-			print_error("Problem getting a socket.");
++			print_error("Problem getting a socket");
+ 	}
+ }
+ 
+@@ -60,7 +54,8 @@
+ 	new->nentries = u_repl->nentries;
+ 	new->num_counters = u_repl->num_counters;
+ 	new->counters = u_repl->counters;
+-	memcpy(new->counter_entry, u_repl->counter_entry, sizeof(new->counter_entry));
++	memcpy(new->counter_entry, u_repl->counter_entry,
++	   sizeof(new->counter_entry));
+ 	// determine size
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if (!(new->valid_hooks & (1 << i)))
+@@ -86,7 +81,8 @@
+ 		}
+ 		// a little sanity check
+ 		if (j != u_repl->hook_entry[i]->nentries)
+-			print_bug("Wrong nentries: %d != %d, hook = %s", j, u_repl->hook_entry[i]->nentries, hooknames[i]);
++			print_bug("Wrong nentries: %d != %d, hook = %s", j,
++			   u_repl->hook_entry[i]->nentries, hooknames[i]);
+ 	}
+ 
+ 	new->entries_size = entries_size;
+@@ -116,7 +112,8 @@
+ 			tmp->ethproto = e->ethproto;
+ 			memcpy(tmp->in, e->in, sizeof(tmp->in));
+ 			memcpy(tmp->out, e->out, sizeof(tmp->out));
+-			memcpy(tmp->sourcemac, e->sourcemac, sizeof(tmp->sourcemac));
++			memcpy(tmp->sourcemac, e->sourcemac,
++			   sizeof(tmp->sourcemac));
+ 			memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
+ 
+ 			base = p;
+@@ -159,11 +156,13 @@
+ 	// give the data to the kernel
+ 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+-		print_error("Couldn't update kernel chains, you probably need to insmod an extension.");	
++		print_error("Couldn't update kernel chains, you probably need "
++		   "to insmod an extension");	
+ }
+ 
+ // gets executed after deliver_table
+-void deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
++void
++deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
+ {
+ 	unsigned short *point;
+ 	struct ebt_counter *old, *new, *newcounters;
+@@ -173,7 +172,8 @@
+ 	if (u_repl->nentries == 0)
+ 		return;
+ 
+-	newcounters = (struct ebt_counter *)malloc(u_repl->nentries * sizeof(struct ebt_counter));
++	newcounters = (struct ebt_counter *)
++	   malloc(u_repl->nentries * sizeof(struct ebt_counter));
+ 	if (!newcounters)
+ 		print_memory();
+ 	memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
+@@ -208,7 +208,8 @@
+ 	free(u_repl->counters);
+ 	u_repl->counters = newcounters;
+ 	u_repl->num_counters = u_repl->nentries;
+-	optlen = u_repl->nentries * sizeof(struct ebt_counter) + sizeof(struct ebt_replace);
++	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
++	   sizeof(struct ebt_replace);
+ 	// now put the stuff in the kernel's struct ebt_replace
+ 	repl.counters = u_repl->counters;
+ 	repl.num_counters = u_repl->num_counters;
+@@ -224,7 +225,8 @@
+ {
+ 	struct ebt_u_match_list *new;
+ 
+-	new = (struct ebt_u_match_list *)malloc(sizeof(struct ebt_u_match_list));
++	new = (struct ebt_u_match_list *)
++	   malloc(sizeof(struct ebt_u_match_list));
+ 	if (!new)
+ 		print_memory();
+ 	new->m = (struct ebt_entry_match *)malloc(m->match_size);
+@@ -235,12 +237,14 @@
+ 	**l = new;
+ 	*l = &new->next;
+ 	if (find_match(new->m->u.name) == NULL)
+-		print_error("Kernel match %s unsupported by userspace tool.", new->m->u.name);
++		print_error("Kernel match %s unsupported by userspace tool",
++		   new->m->u.name);
+ 	return 0;
+ }
+ 
+ static int
+-ebt_translate_watcher(struct ebt_entry_watcher *w, struct ebt_u_watcher_list ***l)
++ebt_translate_watcher(struct ebt_entry_watcher *w,
++   struct ebt_u_watcher_list ***l)
+ {
+ 	struct ebt_u_watcher_list *new;
+ 
+@@ -255,13 +259,15 @@
+ 	**l = new;
+ 	*l = &new->next;
+ 	if (find_watcher(new->w->u.name) == NULL)
+-		print_error("Kernel watcher %s unsupported by userspace tool.", new->w->u.name);
++		print_error("Kernel watcher %s unsupported by userspace tool",
++		   new->w->u.name);
+ 	return 0;
+ }
+ 
+ static int
+-ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt, int *totalcnt,
+-	   struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl, unsigned int valid_hooks)
++ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
++   int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
++   unsigned int valid_hooks)
+ {
+ 	// an entry
+ 	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+@@ -295,7 +301,8 @@
+ 		if (!new->t)
+ 			print_memory();
+ 		if (find_target(t->u.name) == NULL)
+-			print_error("Kernel target %s unsupported by userspace tool.", t->u.name);
++			print_error("Kernel target %s unsupported by "
++			            "userspace tool", t->u.name);
+ 		memcpy(new->t, t, t->target_size);
+ 
+ 		// I love pointers
+@@ -304,7 +311,7 @@
+ 		(*cnt)++;
+ 		(*totalcnt)++;
+ 		return 0;
+-	} else {// a new chain
++	} else { // a new chain
+ 		int i;
+ 		struct ebt_entries *entries = (struct ebt_entries *)e;
+ 		struct ebt_u_entries *new;
+@@ -319,7 +326,8 @@
+ 			print_bug("Nr of entries in the chain is wrong");
+ 		*n = entries->nentries;
+ 		*cnt = 0;
+-		new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
++		new = (struct ebt_u_entries *)
++		   malloc(sizeof(struct ebt_u_entries));
+ 		if (!new)
+ 			print_memory();
+ 		new->nentries = entries->nentries;
+@@ -344,12 +352,14 @@
+ 	optlen = sizeof(struct ebt_replace);
+ 	strcpy(repl.name, u_repl->name);
+ 	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+-		print_bug("hmm, what is wrong??? bug#3");
++		print_error("A kernel module needed by your command is probably"
++		            " not loaded. Try insmod ebtables or the like");
+ 
+ 	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+ 		print_memory();
+ 	if (repl.nentries) {
+-		if (!(repl.counters = (struct ebt_counter *) malloc(repl.nentries * sizeof(struct ebt_counter))) )
++		if (!(repl.counters = (struct ebt_counter *)
++		   malloc(repl.nentries * sizeof(struct ebt_counter))) )
+ 			print_memory();
+ 	}
+ 	else
+@@ -357,7 +367,8 @@
+ 
+ 	// we want to receive the counters
+ 	repl.num_counters = repl.nentries;
+-	optlen += repl.entries_size + repl.num_counters * sizeof(struct ebt_counter);
++	optlen += repl.entries_size + repl.num_counters *
++	   sizeof(struct ebt_counter);
+ 	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
+ 		print_bug("hmm, what is wrong??? bug#1");
+ 
+@@ -367,12 +378,15 @@
+ 	u_repl->nentries = repl.nentries;
+ 	u_repl->num_counters = repl.num_counters;
+ 	u_repl->counters = repl.counters;
+-	memcpy(u_repl->counter_entry, repl.counter_entry, sizeof(repl.counter_entry));
++	memcpy(u_repl->counter_entry, repl.counter_entry,
++	   sizeof(repl.counter_entry));
+ 	hook = -1;
+ 	i = 0; // holds the expected nr. of entries for the chain
+ 	j = 0; // holds the up to now counted entries for the chain
+-	k = 0; // holds the total nr. of entries, should equal u_repl->nentries afterwards
+-	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
++	k = 0; // holds the total nr. of entries,
++	       // should equal u_repl->nentries afterwards
++	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
++	   &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
+ 	if (k != u_repl->nentries)
+ 		print_bug("Wrong total nentries");
+ }
+@@ -384,7 +398,8 @@
+ 	get_sockfd();
+ 	
+ 	if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
+-		print_error("Sorry, br_db code probably not in kernel, try insmod br_db.");
++		print_error("Sorry, br_db code probably not in kernel, "
++		            "try insmod br_db");
+ }
+ 
+ void get_db(int len, struct brdb_dbentry *db)
+@@ -405,5 +420,6 @@
+ 	get_sockfd();
+ 
+ 	if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB, decision, optlen))
+-		print_error("Sorry, br_db code probably not in kernel, try insmod br_db.");
++		print_error("Sorry, br_db code probably not in kernel, "
++		            "try insmod br_db");
+ }
+--- ebtables-v2.0pre2.001/extensions/ebt_nat.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebt_nat.c	Thu Apr 11 18:12:55 2002
+@@ -52,8 +52,9 @@
+ }
+ 
+ #define OPT_SNAT  0x01
+-static int parse_s(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_target **target)
++static int parse_s(int c, char **argv, int argc,
++   const struct ebt_u_entry *entry, unsigned int *flags,
++   struct ebt_entry_target **target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 
+@@ -62,7 +63,7 @@
+ 		check_option(flags, OPT_SNAT);
+ 		to_source_supplied = 1;
+ 		if (getmac(optarg, natinfo->mac))
+-			print_error("Problem with specified to-source mac.");
++			print_error("Problem with specified to-source mac");
+ 		break;
+ 	default:
+ 	return 0;
+@@ -71,8 +72,9 @@
+ }
+ 
+ #define OPT_DNAT  0x01
+-static int parse_d(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_target **target)
++static int parse_d(int c, char **argv, int argc,
++   const struct ebt_u_entry *entry, unsigned int *flags,
++   struct ebt_entry_target **target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 
+@@ -81,7 +83,8 @@
+ 		check_option(flags, OPT_DNAT);
+ 		to_dest_supplied = 1;
+ 		if (getmac(optarg, natinfo->mac))
+-			print_error("Problem with specified to-destination mac.");
++			print_error("Problem with specified "
++			            "to-destination mac");
+ 		break;
+ 	default:
+ 	return 0;
+@@ -89,44 +92,52 @@
+ 	return 1;
+ }
+ 
+-static void final_check_s(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hook)
++static void final_check_s(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target, const char *name, unsigned int hook)
+ {
+ 	if (hook != NF_BR_POST_ROUTING || strcmp(name, "nat"))
+-		print_error("Wrong chain for SNAT.");
++		print_error("Wrong chain for SNAT");
+ 	if (to_source_supplied == 0)
+-		print_error("No snat address supplied.");
++		print_error("No snat address supplied");
+ 
+ }
+ 
+-static void final_check_d(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hook)
++static void final_check_d(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target, const char *name, unsigned int hook)
+ {
+-	if ( (hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) || strcmp(name, "nat") )
+-		print_error("Wrong chain for DNAT.");
++	if ( (hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
++	   strcmp(name, "nat") )
++		print_error("Wrong chain for DNAT");
+ 	if (to_dest_supplied == 0)
+-		print_error("No dnat address supplied.");
++		print_error("No dnat address supplied");
+ }
+ 
+-static void print_s(const struct ebt_u_entry *entry, const struct ebt_entry_target *target)
++static void print_s(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 	int i;
+ 
+ 	printf("snat - to: ");
+ 	for (i = 0; i < ETH_ALEN; i++)
+-		printf("%02x%s", natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++		printf("%02x%s",
++		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
+ }
+ 
+-static void print_d(const struct ebt_u_entry *entry, const struct ebt_entry_target *target)
++static void print_d(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 	int i;
+ 
+ 	printf("dnat - to: ");
+ 	for (i = 0; i < ETH_ALEN; i++)
+-		printf("%02x%s", natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++		printf("%02x%s",
++		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
+ }
+ 
+-static int compare(const struct ebt_entry_target *t1, const struct ebt_entry_target *t2)
++static int compare(const struct ebt_entry_target *t1,
++   const struct ebt_entry_target *t2)
+ {
+ 	struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data;
+ 	struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data;
+--- ebtables-v2.0pre2.001/extensions/ebt_ip.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebt_ip.c	Wed Apr 10 23:28:40 2002
+@@ -40,16 +40,16 @@
+ 			return -1;
+ 		*q = '\0';
+ 		onebyte = strtol(p, &end, 10);
+-		if (*end != '\0' || onebyte >255 || onebyte < 0)
++		if (*end != '\0' || onebyte > 255 || onebyte < 0)
+ 			return -1;
+-		ip2[i] = (unsigned char) onebyte;
++		ip2[i] = (unsigned char)onebyte;
+ 		p = q + 1;
+ 	}
+ 
+ 	onebyte = strtol(p, &end, 10);
+ 	if (*end != '\0' || onebyte >255 || onebyte < 0)
+ 		return -1;
+-	ip2[3] = (unsigned char) onebyte;
++	ip2[3] = (unsigned char)onebyte;
+ 
+ 	return 0;
+ }
+@@ -88,15 +88,15 @@
+ 		*p = '\0';
+ 		i = ip_mask(p + 1, (unsigned char *)msk);
+ 		if (i)
+-			print_error("Problem with the ip mask.");
++			print_error("Problem with the ip mask");
+ 	}
+ 	else
+ 		*msk = 0xFFFFFFFF;
+ 
+ 	i = undot_ip(address, (unsigned char *)addr);
+-	*addr = *addr & *msk;
+ 	if (i)
+-		print_error("Problem with the ip address.");
++		print_error("Problem with the ip address");
++	*addr = *addr & *msk;
+ }
+ 
+ // transform the ip mask into a string ready for output
+@@ -106,7 +106,6 @@
+ 	static char buf[20];
+ 	__u32 maskaddr, bits;
+ 
+-	// cool hack I copied from iptables.c ... Think about it :-)
+ 	maskaddr = ntohl(mask);
+ 
+ 	// don't print /32
+@@ -114,7 +113,7 @@
+ 		return "";
+ 
+ 	i = 32;
+-	bits = 0xFFFFFFFEL;// case 0xFFFFFFFF has just been dealt with
++	bits = 0xFFFFFFFEL; // case 0xFFFFFFFF has just been dealt with
+ 	while (--i >= 0 && maskaddr != bits)
+ 		bits <<= 1;
+ 
+@@ -123,9 +122,10 @@
+ 	else if (!i)
+ 		*buf = '\0';
+ 	else
+-		/* mask was not a decent combination of 1's and 0's */
+-		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], ((unsigned char *)&mask)[1],
+-		        ((unsigned char *)&mask)[2], ((unsigned char *)&mask)[3]);
++		// mask was not a decent combination of 1's and 0's
++		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
++		   ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
++		   ((unsigned char *)&mask)[3]);
+ 
+ 	return buf;
+ }
+@@ -133,11 +133,11 @@
+ static void print_help()
+ {
+ 	printf(
+-	"ip options:\n"
+-	"--ip-src    [!] address[/mask]: ip source specification\n"
+-	"--ip-dst    [!] address[/mask]: ip destination specification\n"
+-	"--ip-tos    [!] tos           : ip tos specification\n"
+-	"--ip-proto  [!] protocol      : ip protocol specification\n");
++"ip options:\n"
++"--ip-src    [!] address[/mask]: ip source specification\n"
++"--ip-dst    [!] address[/mask]: ip destination specification\n"
++"--ip-tos    [!] tos           : ip tos specification\n"
++"--ip-proto  [!] protocol      : ip protocol specification\n");
+ }
+ 
+ static void init(struct ebt_entry_match *match)
+@@ -152,9 +152,8 @@
+ #define OPT_DEST   0x02
+ #define OPT_TOS    0x04
+ #define OPT_PROTO  0x08
+-static int parse(int c, char **argv, int argc,
+-	        const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_match **match)
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+ 	char *end, *buffer;
+@@ -163,13 +162,13 @@
+ 	switch (c) {
+ 	case IP_SOURCE:
+ 		check_option(flags, OPT_SOURCE);
++		ipinfo->bitmask |= EBT_IP_SOURCE;
++
+ 	case IP_DEST:
+-		if (c == IP_DEST)
++		if (c == IP_DEST) {
+ 			check_option(flags, OPT_DEST);
+-		if (c == IP_SOURCE)
+-			ipinfo->bitmask |= EBT_IP_SOURCE;
+-		else
+ 			ipinfo->bitmask |= EBT_IP_DEST;
++		}
+ 		if (check_inverse(optarg)) {
+ 			if (c == IP_SOURCE)
+ 				ipinfo->invflags |= EBT_IP_SOURCE;
+@@ -178,34 +177,38 @@
+ 		}
+ 
+ 		if (optind > argc)
+-			print_error("Missing ip address argument.");
++			print_error("Missing ip address argument");
+ 		if (c == IP_SOURCE)
+-			parse_ip_address(argv[optind - 1], &ipinfo->saddr, &ipinfo->smsk);
++			parse_ip_address(argv[optind - 1], &ipinfo->saddr,
++			   &ipinfo->smsk);
+ 		else
+-			parse_ip_address(argv[optind - 1], &ipinfo->daddr, &ipinfo->dmsk);
++			parse_ip_address(argv[optind - 1], &ipinfo->daddr,
++			   &ipinfo->dmsk);
+ 		break;
++
+ 	case IP_myTOS:
+ 		check_option(flags, OPT_TOS);
+ 		if (check_inverse(optarg))
+ 			ipinfo->invflags |= EBT_IP_TOS;
+ 
+ 		if (optind > argc)
+-			print_error("Missing ip tos argument.");
++			print_error("Missing ip tos argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+ 		if (i < 0 || i > 255 || *buffer != '\0')
+-			print_error("Problem with specified ip tos.");
++			print_error("Problem with specified ip tos");
+ 		ipinfo->tos = i;
+ 		ipinfo->bitmask |= EBT_IP_TOS;
+ 		break;
++
+ 	case IP_PROTO:
+ 		check_option(flags, OPT_PROTO);
+ 		if (check_inverse(optarg))
+ 			ipinfo->invflags |= EBT_IP_PROTO;
+ 		if (optind > argc)
+-			print_error("Missing ip protocol argument.");
++			print_error("Missing ip protocol argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+ 		if (i < 0 || i > 255 || *end != '\0')
+-			print_error("Problem with specified ip protocol.");
++			print_error("Problem with specified ip protocol");
+ 		ipinfo->protocol = i;
+ 		ipinfo->bitmask |= EBT_IP_PROTO;
+ 		break;
+@@ -215,13 +218,17 @@
+ 	return 1;
+ }
+ 
+-static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hook)
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match, const char *name, unsigned int hook)
+ {
+-	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 || entry->ethproto != ETH_P_IP)
+-		print_error("For IP filtering the protocol must be specified as IPV4.");
++	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
++	   entry->ethproto != ETH_P_IP)
++		print_error("For IP filtering the protocol must be "
++		            "specified as IPv4");
+ }
+ 
+-static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match)
+ {
+ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+ 	int j;
+@@ -231,7 +238,8 @@
+ 		if (ipinfo->invflags & EBT_IP_SOURCE)
+ 			printf("! ");
+ 		for (j = 0; j < 4; j++)
+-			printf("%d%s", ((unsigned char *)&ipinfo->saddr)[j], (j == 3) ? "" : ".");
++			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
++			   (j == 3) ? "" : ".");
+ 		printf("%s, ", mask_to_dotted(ipinfo->smsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_DEST) {
+@@ -239,7 +247,8 @@
+ 		if (ipinfo->invflags & EBT_IP_DEST)
+ 			printf("! ");
+ 		for (j = 0; j < 4; j++)
+-			printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j], (j == 3) ? "" : ".");
++			printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
++			   (j == 3) ? "" : ".");
+ 		printf("%s, ", mask_to_dotted(ipinfo->dmsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_TOS) {
+@@ -256,7 +265,8 @@
+ 	}
+ }
+ 
+-static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2)
++static int compare(const struct ebt_entry_match *m1,
++   const struct ebt_entry_match *m2)
+ {
+ 	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
+ 	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
+--- ebtables-v2.0pre2.001/extensions/ebt_arp.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebt_arp.c	Wed Apr 10 23:05:29 2002
+@@ -44,20 +44,20 @@
+ 	int i = 0;
+ 
+ 	printf(
+-	"arp options:\n"
+-	"--arp-opcode opcode            : ARP opcode (integer or string)\n"
+-	"--arp-htype type               : ARP hardware type (integer or string)\n"
+-	"--arp-ptype type               : ARP protocol type (hexadecimal or string)\n"
+-	"--arp-ip-src [!] address[/mask]: ARP ip source specification\n"
+-	"--arp-ip-dst [!] address[/mask]: ARP ip target specification\n"
+-	" opcode strings: \n");
++"arp options:\n"
++"--arp-opcode opcode            : ARP opcode (integer or string)\n"
++"--arp-htype type               : ARP hardware type (integer or string)\n"
++"--arp-ptype type               : ARP protocol type (hexadecimal or string)\n"
++"--arp-ip-src [!] address[/mask]: ARP ip source specification\n"
++"--arp-ip-dst [!] address[/mask]: ARP ip target specification\n"
++" opcode strings: \n");
+ 	while (strcmp(opcodes[i], "")) {
+ 		printf("%d = %s\n", i + 1, opcodes[i]);
+ 		i++;
+ 	}
+ 	printf(
+-	" hardware type string: \n 1 = Ethernet\n"
+-	" protocol type string: \n 0x0800 = IPv4\n");
++" hardware type string: \n 1 = Ethernet\n"
++" protocol type string: \n 0x0800 = IPv4\n");
+ }
+ 
+ static void init(struct ebt_entry_match *match)
+@@ -68,7 +68,8 @@
+ 	arpinfo->bitmask = 0;
+ }
+ 
+-void parse_ip_address(char *address, __u32 *addr, __u32 *msk); // defined in ebt_ip.c
++// defined in ebt_ip.c
++void parse_ip_address(char *address, __u32 *addr, __u32 *msk);
+ 
+ #define OPT_OPCODE 0x01
+ #define OPT_HTYPE  0x02
+@@ -87,15 +88,14 @@
+ 
+ 	switch (c) {
+ 	case ARP_OPCODE:
+-
+ 		check_option(flags, OPT_OPCODE);
+ 		if (check_inverse(optarg))
+ 			arpinfo->invflags |= EBT_ARP_OPCODE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp opcode argument.");
++			print_error("Missing arp opcode argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+-		if (i < 0 || i > (0x1 << 16) || *end !='\0') {
++		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+ 			i = 0;
+ 			while (strcmp(opcodes[i], "")) {
+ 				if (!strcasecmp(opcodes[i], optarg))
+@@ -103,7 +103,8 @@
+ 				i++;
+ 			}
+ 			if (!strcmp(opcodes[i], ""))
+-				print_error("Problem with specified arp opcode.");
++				print_error("Problem with specified "
++				            "arp opcode");
+ 		}
+ 		arpinfo->opcode = htons(i);
+ 		arpinfo->bitmask |= EBT_ARP_OPCODE;
+@@ -115,13 +116,14 @@
+ 			arpinfo->invflags |= EBT_ARP_HTYPE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp hardware type argument.");
++			print_error("Missing arp hardware type argument");
+ 		i = strtol(argv[optind - 1], &end, 10);
+-		if (i < 0 || i > (0x1 << 16) || *end !='\0') {
++		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+ 			if (!strcasecmp("Ethernet", argv[optind - 1]))
+ 				i = 1;
+ 			else
+-				print_error("Problem with specified arp hardware type.");
++				print_error("Problem with specified arp "
++				            "hardware type");
+ 		}
+ 		arpinfo->htype = htons(i);
+ 		arpinfo->bitmask |= EBT_ARP_HTYPE;
+@@ -133,13 +135,14 @@
+ 			arpinfo->invflags |= EBT_ARP_PTYPE;
+ 
+ 		if (optind > argc)
+-			print_error("Missing arp protocol type argument.");
++			print_error("Missing arp protocol type argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+-		if (i < 0 || i > (0x1 << 16) || *end !='\0') {
++		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+ 			if (!strcasecmp("IPv4", argv[optind - 1]))
+ 				i = 0x0800;
+ 			else
+-				print_error("Problem with specified arp protocol type.");
++				print_error("Problem with specified arp "
++				            "protocol type");
+ 		}
+ 		arpinfo->ptype = htons(i);
+ 		arpinfo->bitmask |= EBT_ARP_PTYPE;
+@@ -165,7 +168,7 @@
+ 				arpinfo->invflags |= EBT_ARP_DST_IP;
+ 		}
+ 		if (optind > argc)
+-			print_error("Missing ip address argument.");
++			print_error("Missing ip address argument");
+ 		parse_ip_address(argv[optind - 1], addr, mask);
+ 		break;
+ 	default:
+@@ -174,14 +177,19 @@
+ 	return 1;
+ }
+ 
+-static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hook)
++static void final_check(const struct ebt_u_entry *entry,
++const struct ebt_entry_match *match, const char *name, unsigned int hook)
+ {
+-	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 || (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
+-		print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP.");
++	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
++	   (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
++		print_error("For (R)ARP filtering the protocol must be "
++		            "specified as ARP or RARP");
+ }
+ 
+-char *mask_to_dotted(__u32 mask); // defined in the ebt_ip.c
+-static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
++// defined in the ebt_ip.c
++char *mask_to_dotted(__u32 mask);
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match)
+ {
+ 	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+ 	int i;
+@@ -209,7 +217,8 @@
+ 		if (arpinfo->invflags & EBT_ARP_SRC_IP)
+ 			printf("! ");
+ 		for (i = 0; i < 4; i++)
+-			printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i], (i == 3) ? "" : ".");
++			printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
++			   (i == 3) ? "" : ".");
+ 		printf("%s, ", mask_to_dotted(arpinfo->smsk));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+@@ -217,12 +226,14 @@
+ 		if (arpinfo->invflags & EBT_ARP_DST_IP)
+ 			printf("! ");
+ 		for (i = 0; i < 4; i++)
+-			printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i], (i == 3) ? "" : ".");
++			printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
++			   (i == 3) ? "" : ".");
+ 		printf("%s, ", mask_to_dotted(arpinfo->dmsk));
+ 	}
+ }
+ 
+-static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2)
++static int compare(const struct ebt_entry_match *m1,
++   const struct ebt_entry_match *m2)
+ {
+ 	struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data;
+ 	struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data;
+--- ebtables-v2.0pre2.001/extensions/ebt_log.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebt_log.c	Wed Apr 10 23:51:18 2002
+@@ -9,14 +9,14 @@
+ 
+ // copied from syslog.h
+ // used for the LOG target
+-#define	LOG_EMERG	0	/* system is unusable */
+-#define	LOG_ALERT	1	/* action must be taken immediately */
+-#define	LOG_CRIT	2	/* critical conditions */
+-#define	LOG_ERR		3	/* error conditions */
+-#define	LOG_WARNING	4	/* warning conditions */
+-#define	LOG_NOTICE	5	/* normal but significant condition */
+-#define	LOG_INFO	6	/* informational */
+-#define	LOG_DEBUG	7	/* debug-level messages */
++#define	LOG_EMERG	0 // system is unusable
++#define	LOG_ALERT	1 // action must be taken immediately
++#define	LOG_CRIT	2 // critical conditions
++#define	LOG_ERR		3 // error conditions
++#define	LOG_WARNING	4 // warning conditions
++#define	LOG_NOTICE	5 // normal but significant condition
++#define	LOG_INFO	6 // informational
++#define	LOG_DEBUG	7 // debug-level messages
+ #define LOG_DEFAULT_LEVEL LOG_INFO
+ 
+ typedef struct _code {
+@@ -70,16 +70,17 @@
+ 	int i;
+ 
+ 	printf(
+-	"log options:\n"
+-	"--log               : use this if you're not specifying anything\n"
+-	"--log-level level   : level = [1-8] or a string\n"
+-	"--log-prefix prefix : max. %d chars.\n"
+-	"--log-ip            : put ip info. in the log for ip packets\n"
+-	"--log-arp           : put (r)arp info. in the log for (r)arp packets\n"
++"log options:\n"
++"--log               : use this if you're not specifying anything\n"
++"--log-level level   : level = [1-8] or a string\n"
++"--log-prefix prefix : max. %d chars.\n"
++"--log-ip            : put ip info. in the log for ip packets\n"
++"--log-arp           : put (r)arp info. in the log for (r)arp packets\n"
+ 	, EBT_LOG_PREFIX_SIZE - 1);
+ 	printf("levels:\n");
+ 	for (i = 0; i < 8; i++)
+-		printf("%d = %s\n", eight_priority[i].c_val, eight_priority[i].c_name);
++		printf("%d = %s\n", eight_priority[i].c_val,
++		   eight_priority[i].c_name);
+ }
+ 
+ static void init(struct ebt_entry_watcher *watcher)
+@@ -88,7 +89,7 @@
+ 
+ 	loginfo->bitmask = 0;
+ 	loginfo->prefix[0] = '\0';
+-	loginfo->loglevel = 6;
++	loginfo->loglevel = LOG_NOTICE;
+ }
+ 
+ #define OPT_PREFIX 0x01
+@@ -96,9 +97,8 @@
+ #define OPT_ARP    0x04
+ #define OPT_IP     0x08
+ #define OPT_LOG    0x10
+-static int parse(int c, char **argv, int argc,
+-	        const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_watcher **watcher)
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_watcher **watcher)
+ {
+ 	struct ebt_log_info *loginfo = (struct ebt_log_info *)(*watcher)->data;
+ 	int i;
+@@ -108,9 +108,10 @@
+ 	case LOG_PREFIX:
+ 		check_option(flags, OPT_PREFIX);
+ 		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+-			print_error("Prefix too long.");
++			print_error("Prefix too long");
+ 		strcpy(loginfo->prefix, optarg);
+ 		break;
++
+ 	case LOG_LEVEL:
+ 		check_option(flags, OPT_LEVEL);
+ 		i = strtol(optarg, &end, 16);
+@@ -119,16 +120,19 @@
+ 		else
+ 			loginfo->loglevel = i;
+ 		if (loginfo->loglevel == 9)
+-			print_error("Problem with the log-level.");
++			print_error("Problem with the log-level");
+ 		break;
++
+ 	case LOG_IP:
+ 		check_option(flags, OPT_IP);
+ 		loginfo->bitmask |= EBT_LOG_IP;
+ 		break;
++
+ 	case LOG_ARP:
+ 		check_option(flags, OPT_ARP);
+ 		loginfo->bitmask |= EBT_LOG_ARP;
+ 		break;
++
+ 	case LOG_LOG:
+ 		check_option(flags, OPT_LOG);
+ 		break;
+@@ -138,16 +142,18 @@
+ 	return 1;
+ }
+ 
+-static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watcher, const char *name, unsigned int hook)
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_watcher *watcher, const char *name, unsigned int hook)
+ {
+ 	return;
+ }
+ 
+-static void print(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watcher)
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_watcher *watcher)
+ {
+ 	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+ 
+-	printf("log: log-level = %s - log-prefix = '%s'",
++	printf("log: log-level = %s - log-prefix = \"%s\"",
+ 		eight_priority[loginfo->loglevel].c_name,
+ 		loginfo->prefix);
+ 	if (loginfo->bitmask & EBT_LOG_IP)
+@@ -157,7 +163,8 @@
+ 	printf(" ");
+ }
+ 
+-static int compare(const struct ebt_entry_watcher *w1, const struct ebt_entry_watcher *w2)
++static int compare(const struct ebt_entry_watcher *w1,
++   const struct ebt_entry_watcher *w2)
+ {
+ 	struct ebt_log_info *loginfo1 = (struct ebt_log_info *)w1->data;
+ 	struct ebt_log_info *loginfo2 = (struct ebt_log_info *)w2->data;
+--- ebtables-v2.0pre2.001/extensions/ebt_standard.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebt_standard.c	Thu Apr 11 18:14:07 2002
+@@ -19,17 +19,19 @@
+ 	((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE;
+ }
+ 
+-static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_target **target)
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_target **target)
+ {
+ 	return 0;
+ }
+ 
+-static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hook)
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target, const char *name, unsigned int hook)
+ {
+ }
+ 
+-static void print(const struct ebt_u_entry *entry, const struct ebt_entry_target *target)
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target)
+ {
+ 	__u8 verdict = ((struct ebt_standard_target *)target)->verdict;
+ 
+@@ -41,9 +43,11 @@
+ 		printf("Drop ");
+ }
+ 
+-static int compare(const struct ebt_entry_target *t1, const struct ebt_entry_target *t2)
++static int compare(const struct ebt_entry_target *t1,
++   const struct ebt_entry_target *t2)
+ {
+-	return ((struct ebt_standard_target *)t1)->verdict == ((struct ebt_standard_target *)t2)->verdict;
++	return ((struct ebt_standard_target *)t1)->verdict ==
++	   ((struct ebt_standard_target *)t2)->verdict;
+ }
+ 
+ static struct ebt_u_target standard =
+--- ebtables-v2.0pre2.001/extensions/ebtable_filter.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebtable_filter.c	Thu Apr 11 18:14:40 2002
+@@ -3,7 +3,8 @@
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include "../include/ebtables_u.h"
+ 
+-#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT))
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++   (1 << NF_BR_LOCAL_OUT))
+ 
+ static void print_help(char **hn)
+ {
+--- ebtables-v2.0pre2.001/extensions/ebtable_nat.c	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/extensions/ebtable_nat.c	Thu Apr 11 18:14:57 2002
+@@ -2,7 +2,8 @@
+ #include <sys/socket.h>
+ #include "../include/ebtables_u.h"
+ 
+-#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | (1 << NF_BR_POST_ROUTING))
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++   (1 << NF_BR_POST_ROUTING))
+ 
+ static void print_help(char **hn)
+ {
+--- ebtables-v2.0pre2.001/ChangeLog	Wed Apr  3 16:56:37 2002
++++ ebtables-v2.0pre2.002/ChangeLog	Thu Apr 11 18:26:21 2002
+@@ -1,3 +1,12 @@
++20020411
++	* -j standard no longer works, is this cryptic? good :)
++	* lots of beautification.
++	  - made some code smaller
++	  - made everything fit within 80 columns
++	* fix problems with -i and -o option
++	* print_memory now prints useful info
++	* trying to see the tables when ebtables is not loaded in kernel
++	  no longer makes this be seen as a bug.
+ 20020403
+ 	ebtables v2.0 released, changes:
+ 	* A complete rewrite, made everything modular.
+--- ebtables-v2.0pre2.001/include/ebtables_u.h	Thu Apr 11 18:27:45 2002
++++ ebtables-v2.0pre2.002/include/ebtables_u.h	Wed Apr 10 22:29:01 2002
+@@ -38,20 +38,28 @@
+ {
+ 	char name[EBT_TABLE_MAXNAMELEN];
+ 	unsigned int valid_hooks;
+-	unsigned int nentries; // nr of rules in the table
++	// nr of rules in the table
++	unsigned int nentries;
+ 	struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
+-	unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it?
+-	unsigned int num_counters; // nr of counters the userspace expects back
+-	struct ebt_counter *counters; // where the kernel will put the old counters
+-	unsigned int flags; // can be used e.g. to know if a standard option has been specified twice
+-	char command; // we stick the specified command (e.g. -A) in here
+-	int selected_hook; // here we stick the hook to do our thing on (can be -1 if unspecified)
++	// how many counters in front of it?
++	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// nr of counters userspace expects back
++	unsigned int num_counters;
++	// where the kernel will put the old counters
++	struct ebt_counter *counters;
++	// can be used e.g. to know if a standard option
++	// has been specified twice
++	unsigned int flags;
++	// we stick the specified command (e.g. -A) in here
++	char command;
++	// here we stick the hook to do our thing on (can be -1 if unspecified)
++	int selected_hook;
+ };
+ 
+ struct ebt_u_table
+ {
+ 	char name[EBT_TABLE_MAXNAMELEN];
+-	int (*check) (struct ebt_u_replace *repl);
++	int (*check)(struct ebt_u_replace *repl);
+ 	void (*help)(char **);
+ 	struct ebt_u_table *next;
+ };
+@@ -70,9 +78,9 @@
+ 
+ struct ebt_u_entry
+ {
+-	__u32 bitmask; // this needs to be the first field
++	__u32 bitmask;
+ 	__u32 invflags;
+-	__u16 ethproto;  /* packet type ID field	*/
++	__u16 ethproto;
+ 	__u8 in[IFNAMSIZ];
+ 	__u8 out[IFNAMSIZ];
+ 	__u8 sourcemac[ETH_ALEN];
+@@ -86,35 +94,47 @@
+ struct ebt_u_match
+ {
+ 	char name[EBT_FUNCTION_MAXNAMELEN];
+-	unsigned int size;// size of the real match data
++	// size of the real match data + sizeof struct ebt_match
++	unsigned int size;
+ 	void (*help)(void);
+ 	void (*init)(struct ebt_entry_match *m);
+ 	int (*parse)(int c, char **argv, int argc,
+ 	        const struct ebt_u_entry *entry, unsigned int *flags,
+ 	        struct ebt_entry_match **match);
+-	void (*final_check)(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hook);
+-	void (*print)(const struct ebt_u_entry *entry, const struct ebt_entry_match *match);
+-	int (*compare)(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2);
++	void (*final_check)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_match *match,
++	   const char *name, unsigned int hook);
++	void (*print)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_match *match);
++	int (*compare)(const struct ebt_entry_match *m1,
++	   const struct ebt_entry_match *m2);
+ 	const struct option *extra_ops;
+-	unsigned int flags;// can be used e.g. to check for multiple occurance of the same option
++	// can be used e.g. to check for multiple occurance of the same option
++	unsigned int flags;
+ 	unsigned int option_offset;
+ 	struct ebt_entry_match *m;
+-	unsigned int used;// if used == 1 we no longer have to add it to the match chain of the new entry
++	// if used == 1 we no longer have to add it to
++	// the match chain of the new entry
++	unsigned int used;
+ 	struct ebt_u_match *next;
+ };
+ 
+ struct ebt_u_watcher
+ {
+ 	char name[EBT_FUNCTION_MAXNAMELEN];
+-	unsigned int size;// size of the real match data
++	unsigned int size;
+ 	void (*help)(void);
+ 	void (*init)(struct ebt_entry_watcher *w);
+ 	int (*parse)(int c, char **argv, int argc,
+-	        const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_watcher **watcher);
+-	void (*final_check)(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watch, const char *name, unsigned int hook);
+-	void (*print)(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watcher);
+-	int (*compare)(const struct ebt_entry_watcher *w1, const struct ebt_entry_watcher *w2);
++	   const struct ebt_u_entry *entry, unsigned int *flags,
++	   struct ebt_entry_watcher **watcher);
++	void (*final_check)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_watcher *watch, const char *name,
++	   unsigned int hook);
++	void (*print)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_watcher *watcher);
++	int (*compare)(const struct ebt_entry_watcher *w1,
++	   const struct ebt_entry_watcher *w2);
+ 	const struct option *extra_ops;
+ 	unsigned int flags;
+ 	unsigned int option_offset;
+@@ -126,14 +146,19 @@
+ struct ebt_u_target
+ {
+ 	char name[EBT_FUNCTION_MAXNAMELEN];
+-	unsigned int size;// size of the real match data
++	unsigned int size;
+ 	void (*help)(void);
+ 	void (*init)(struct ebt_entry_target *t);
+-	int (*parse)(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_target **target);
+-	void (*final_check)(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hook);
+-	void (*print)(const struct ebt_u_entry *entry, const struct ebt_entry_target *target);
+-	int (*compare)(const struct ebt_entry_target *t1, const struct ebt_entry_target *t2);
++	int (*parse)(int c, char **argv, int argc,
++	   const struct ebt_u_entry *entry, unsigned int *flags,
++	   struct ebt_entry_target **target);
++	void (*final_check)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_target *target, const char *name,
++	   unsigned int hook);
++	void (*print)(const struct ebt_u_entry *entry,
++	   const struct ebt_entry_target *target);
++	int (*compare)(const struct ebt_entry_target *t1,
++	   const struct ebt_entry_target *t2);
+ 	const struct option *extra_ops;
+ 	unsigned int option_offset;
+ 	unsigned int flags;
+@@ -150,19 +175,21 @@
+ struct ebt_u_target *find_target(const char *name);
+ struct ebt_u_match *find_match(const char *name);
+ struct ebt_u_watcher *find_watcher(const char *name);
+-void deliver_counters(struct ebt_u_replace *repl, unsigned short * counterchanges);
++void deliver_counters(struct ebt_u_replace *repl,
++   unsigned short * counterchanges);
+ void deliver_table(struct ebt_u_replace *repl);
+-void get_sockfd();
+ void get_dbinfo(struct brdb_dbinfo *nr);
+ void get_db(int len, struct brdb_dbentry *db);
+ void deliver_allowdb(__u16 *decision);
+-void print_memory();
+-void init_extensions();
+ int getmac(char *from, char *to);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
+-#define print_bug(format, args...) {printf("BUG: "format"\n", ##args); exit(-1);}
+-#define print_error(format, args...) {printf(format"\n", ##args); exit(-1);}
++#define print_bug(format, args...) \
++   {printf("BUG: "format".\n", ##args); exit(-1);}
++#define print_error(format, args...) {printf(format".\n", ##args); exit(-1);}
++#define print_memory() {printf("Ebtables: " __FILE__ " " __FUNCTION__ \
++   " %d :Out of memory.\n", __LINE__); exit(-1);}
++
+ 
+ 
+ // used for keeping the rule counters right during rule adds or deletes
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre2.003.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre2.003.diff
new file mode 100644
index 0000000..aac6a6f
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre2.003.diff
@@ -0,0 +1,40 @@
+--- ebtables-v2.0pre2.002/Makefile	Sat Apr 13 17:39:08 2002
++++ ebtables-v2.0pre2.003/Makefile	Sat Apr 13 17:43:09 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre2.001 (April 2002)"
++PROGVERSION:="2.0pre2.003 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre2.002/ebtables.c	Sat Apr 13 17:39:08 2002
++++ ebtables-v2.0pre2.003/ebtables.c	Sat Apr 13 17:37:00 2002
+@@ -1490,7 +1490,7 @@
+ 		flush_chains();
+ 	else if (replace.command == 'A' || replace.command == 'I')
+ 		add_rule(rule_nr);
+-	else
++	else if (replace.command == 'D')
+ 		delete_rule(rule_nr);
+ 
+ 	if (table->check)
+--- ebtables-v2.0pre2.002/THANKS	Wed Apr  3 17:44:44 2002
++++ ebtables-v2.0pre2.003/THANKS	Sat Apr 13 17:40:35 2002
+@@ -5,3 +5,5 @@
+ Harald Welte
+ Jason Lunz
+ Tim Gardner
++Loïc Minier
++
+--- ebtables-v2.0pre2.002/ebtables.8	Wed Apr  3 16:29:11 2002
++++ ebtables-v2.0pre2.003/ebtables.8	Sat Apr 13 17:32:23 2002
+@@ -128,6 +128,7 @@
+ Put the counters of the selected chain on zero. If no chain is selected, all the counters
+ are put on zero. This can be used in conjunction with the -L command (see above). 
+ This will cause the rule counters to be printed on the screen before they are put on zero.
++.TP
+ .B "-P, --policy"
+ Set the policy for the chain to the given target. The policy is either
+ .B ACCEPT
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre2.004.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre2.004.diff
new file mode 100644
index 0000000..87ed4dc
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre2.004.diff
@@ -0,0 +1,50 @@
+--- ebtables-v2.0pre2.003/Makefile	Sun Apr 14 15:01:46 2002
++++ ebtables-v2.0pre2.004/Makefile	Sun Apr 14 15:03:11 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre2.003 (April 2002)"
++PROGVERSION:="2.0pre2 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre2.003/ChangeLog	Sat Apr 13 17:39:08 2002
++++ ebtables-v2.0pre2.004/ChangeLog	Sun Apr 14 14:15:59 2002
+@@ -1,3 +1,6 @@
++20020414
++	* fixed some things in the manual.
++	* fixed -P problem.
+ 20020411
+ 	* -j standard no longer works, is this cryptic? good :)
+ 	* lots of beautification.
+--- ebtables-v2.0pre2.003/ebtables.8	Sun Apr 14 15:01:46 2002
++++ ebtables-v2.0pre2.004/ebtables.8	Sun Apr 14 14:58:15 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "03 April 2002"
++.TH EBTABLES 8  "14 April 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -21,7 +21,7 @@
+ .\"     
+ .\"
+ .SH NAME
+-ebtables(v.2) \- ethernet bridge packet table administration
++ebtables(v.2.0) \- ethernet bridge packet table administration
+ .SH SYNOPSIS
+ .BR "ebtables -[ADI] " "chain rule-specification [options]"
+ .br
+@@ -263,6 +263,12 @@
+ .BR "--arp-ptype " "[!] \fIprotocol type\fP"
+ The protocol type for which the (r)arp is used (hexadecimal or the string "IPv4").
+ This is normally IPv4 (0x0800). 
++.TP
++.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP IP source address specification.
++.TP
++.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP IP destination address specification.
+ .SH WATCHER EXTENSION(S)
+ Watchers are things that only look at frames passing by. These watchers only see the
+ frame if the frame passes all the matches of the rule.
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre3.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre3.001.diff
new file mode 100644
index 0000000..12c7a2b
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre3.001.diff
@@ -0,0 +1,245 @@
+Adjust to the kernel's wish that the size members represent the size
+of the actual data.
+
+Make a little note in the man page about the chain naming.
+
+--- ebtables-v2.0pre2/ebtables.c	Sat Apr 13 17:37:00 2002
++++ ebtables-v2.0pre3.001/ebtables.c	Fri Apr 19 19:44:33 2002
+@@ -253,14 +253,14 @@
+ 
+ void register_match(struct ebt_u_match *m)
+ {
+-	int size = m->size;
++	int size = m->size + sizeof(struct ebt_entry_match);
+ 	struct ebt_u_match **i;
+ 
+ 	m->m = (struct ebt_entry_match *)malloc(size);
+ 	if (!m->m)
+ 		print_memory();
+ 	strcpy(m->m->u.name, m->name);
+-	m->m->match_size = size;
++	m->m->match_size = m->size;
+ 	ebt_options = merge_options
+ 	   (ebt_options, m->extra_ops, &(m->option_offset));
+ 	m->init(m->m);
+@@ -272,14 +272,14 @@
+ 
+ void register_watcher(struct ebt_u_watcher *w)
+ {
+-	int size = w->size;
++	int size = w->size + sizeof(struct ebt_entry_watcher);
+ 	struct ebt_u_watcher **i;
+ 
+ 	w->w = (struct ebt_entry_watcher *)malloc(size);
+ 	if (!w->w)
+ 		print_memory();
+ 	strcpy(w->w->u.name, w->name);
+-	w->w->watcher_size = size;
++	w->w->watcher_size = w->size;
+ 	ebt_options = merge_options
+ 	   (ebt_options, w->extra_ops, &(w->option_offset));
+ 	w->init(w->w);
+@@ -291,14 +291,14 @@
+ 
+ void register_target(struct ebt_u_target *t)
+ {
+-	int size = t->size;
++	int size = t->size + sizeof(struct ebt_entry_target);
+ 	struct ebt_u_target **i;
+ 
+ 	t->t = (struct ebt_entry_target *)malloc(size);
+ 	if (!t->t)
+ 		print_memory();
+ 	strcpy(t->t->u.name, t->name);
+-	t->t->target_size = size;
++	t->t->target_size = t->size;
+ 	ebt_options = merge_options
+ 	   (ebt_options, t->extra_ops, &(t->option_offset));
+ 	t->init(t->t);
+--- ebtables-v2.0pre2/communication.c	Wed Apr 10 22:10:49 2002
++++ ebtables-v2.0pre3.001/communication.c	Fri Apr 19 22:02:45 2002
+@@ -68,15 +68,18 @@
+ 			entries_size += sizeof(struct ebt_entry);
+ 			m_l = e->m_list;
+ 			while (m_l) {
+-				entries_size += m_l->m->match_size;
++				entries_size += m_l->m->match_size +
++				   sizeof(struct ebt_entry_match);
+ 				m_l = m_l->next;
+ 			}
+ 			w_l = e->w_list;
+ 			while (w_l) {
+-				entries_size += w_l->w->watcher_size;
++				entries_size += w_l->w->watcher_size +
++				   sizeof(struct ebt_entry_watcher);
+ 				w_l = w_l->next;
+ 			}
+-			entries_size += e->t->target_size;
++			entries_size += e->t->target_size +
++			   sizeof(struct ebt_entry_target);
+ 			e = e->next;
+ 		}
+ 		// a little sanity check
+@@ -120,20 +123,26 @@
+ 			p += sizeof(struct ebt_entry);
+ 			m_l = e->m_list;
+ 			while (m_l) {
+-				memcpy(p, m_l->m, m_l->m->match_size);
+-				p += m_l->m->match_size;
++				memcpy(p, m_l->m, m_l->m->match_size +
++				   sizeof(struct ebt_entry_match));
++				p += m_l->m->match_size +
++				   sizeof(struct ebt_entry_match);
+ 				m_l = m_l->next;
+ 			}
+ 			tmp->watchers_offset = p - base;
+ 			w_l = e->w_list;
+ 			while (w_l) {
+-				memcpy(p, w_l->w, w_l->w->watcher_size);
+-				p += w_l->w->watcher_size;
++				memcpy(p, w_l->w, w_l->w->watcher_size +
++				   sizeof(struct ebt_entry_watcher));
++				p += w_l->w->watcher_size +
++				   sizeof(struct ebt_entry_watcher);
+ 				w_l = w_l->next;
+ 			}
+ 			tmp->target_offset = p - base;
+-			memcpy(p, e->t, e->t->target_size);
+-			p += e->t->target_size;
++			memcpy(p, e->t, e->t->target_size +
++			   sizeof(struct ebt_entry_target));
++			p += e->t->target_size +
++			   sizeof(struct ebt_entry_target);
+ 			tmp->next_offset = p - base;
+ 			e = e->next;
+ 		}
+@@ -229,10 +238,11 @@
+ 	   malloc(sizeof(struct ebt_u_match_list));
+ 	if (!new)
+ 		print_memory();
+-	new->m = (struct ebt_entry_match *)malloc(m->match_size);
++	new->m = (struct ebt_entry_match *)
++	   malloc(m->match_size + sizeof(struct ebt_entry_match));
+ 	if (!new->m)
+ 		print_memory();
+-	memcpy(new->m, m, m->match_size);
++	memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+ 	new->next = NULL;
+ 	**l = new;
+ 	*l = &new->next;
+@@ -248,13 +258,15 @@
+ {
+ 	struct ebt_u_watcher_list *new;
+ 
+-	new = (struct ebt_u_watcher_list *)malloc(sizeof(struct ebt_u_watcher_list));
++	new = (struct ebt_u_watcher_list *)
++	   malloc(sizeof(struct ebt_u_watcher_list));
+ 	if (!new)
+ 		print_memory();
+-	new->w = (struct ebt_entry_watcher *)malloc(w->watcher_size);
++	new->w = (struct ebt_entry_watcher *)
++	   malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
+ 	if (!new->w)
+ 		print_memory();
+-	memcpy(new->w, w, w->watcher_size);
++	memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+ 	new->next = NULL;
+ 	**l = new;
+ 	*l = &new->next;
+@@ -297,13 +309,15 @@
+ 		EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
+ 
+ 		t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+-		new->t = (struct ebt_entry_target *)malloc(t->target_size);
++		new->t = (struct ebt_entry_target *)
++		   malloc(t->target_size + sizeof(struct ebt_entry_target));
+ 		if (!new->t)
+ 			print_memory();
+ 		if (find_target(t->u.name) == NULL)
+ 			print_error("Kernel target %s unsupported by "
+ 			            "userspace tool", t->u.name);
+-		memcpy(new->t, t, t->target_size);
++		memcpy(new->t, t, t->target_size +
++		   sizeof(struct ebt_entry_target));
+ 
+ 		// I love pointers
+ 		**u_e = new;
+@@ -419,7 +433,8 @@
+ 
+ 	get_sockfd();
+ 
+-	if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB, decision, optlen))
++	if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB,
++	   decision, optlen))
+ 		print_error("Sorry, br_db code probably not in kernel, "
+ 		            "try insmod br_db");
+ }
+--- ebtables-v2.0pre2/extensions/ebt_nat.c	Thu Apr 11 18:12:55 2002
++++ ebtables-v2.0pre3.001/extensions/ebt_nat.c	Fri Apr 19 19:41:04 2002
+@@ -161,7 +161,7 @@
+ static struct ebt_u_target dnat_target =
+ {
+ 	EBT_DNAT_TARGET,
+-	sizeof(struct ebt_nat_info) + sizeof(struct ebt_entry_target),
++	sizeof(struct ebt_nat_info),
+ 	print_help_d,
+ 	init_d,
+ 	parse_d,
+--- ebtables-v2.0pre2/extensions/ebt_ip.c	Wed Apr 10 23:28:40 2002
++++ ebtables-v2.0pre3.001/extensions/ebt_ip.c	Fri Apr 19 19:40:18 2002
+@@ -301,7 +301,7 @@
+ static struct ebt_u_match ip_match =
+ {
+ 	EBT_IP_MATCH,
+-	sizeof(struct ebt_ip_info) + sizeof(struct ebt_entry_match),
++	sizeof(struct ebt_ip_info),
+ 	print_help,
+ 	init,
+ 	parse,
+--- ebtables-v2.0pre2/extensions/ebt_arp.c	Wed Apr 10 23:05:29 2002
++++ ebtables-v2.0pre3.001/extensions/ebt_arp.c	Fri Apr 19 19:40:40 2002
+@@ -272,7 +272,7 @@
+ static struct ebt_u_match arp_match =
+ {
+ 	EBT_ARP_MATCH,
+-	sizeof(struct ebt_arp_info) + sizeof(struct ebt_entry_match),
++	sizeof(struct ebt_arp_info),
+ 	print_help,
+ 	init,
+ 	parse,
+--- ebtables-v2.0pre2/extensions/ebt_log.c	Wed Apr 10 23:51:18 2002
++++ ebtables-v2.0pre3.001/extensions/ebt_log.c	Fri Apr 19 19:40:52 2002
+@@ -179,7 +179,7 @@
+ static struct ebt_u_watcher log_watcher =
+ {
+ 	EBT_LOG_WATCHER,
+-	sizeof(struct ebt_log_info) + sizeof(struct ebt_entry_watcher),
++	sizeof(struct ebt_log_info),
+ 	print_help,
+ 	init,
+ 	parse,
+--- ebtables-v2.0pre2/extensions/ebt_standard.c	Thu Apr 11 18:14:07 2002
++++ ebtables-v2.0pre3.001/extensions/ebt_standard.c	Fri Apr 19 19:44:54 2002
+@@ -53,7 +53,7 @@
+ static struct ebt_u_target standard =
+ {
+ 	EBT_STANDARD_TARGET,
+-	sizeof(struct ebt_standard_target),
++	sizeof(struct ebt_standard_target) - sizeof(struct ebt_entry_target),
+ 	print_help,
+ 	init,
+ 	parse,
+--- ebtables-v2.0pre2/ebtables.8	Sun Apr 14 14:58:15 2002
++++ ebtables-v2.0pre3.001/ebtables.8	Thu Apr 18 18:44:40 2002
+@@ -86,7 +86,10 @@
+ .B OUTPUT 
+ (for altering locally generated frames before they are bridged) and 
+ .B POSTROUTING
+-(for altering frames as they are about to go out).
++(for altering frames as they are about to go out). A small note on the naming
++of chains POSTROUTING and PREROUTING: it would be more accurate to call them
++PREFORWARDING and POSTFORWARDING, but for all those who come from the iptables
++world to ebtables it is easier to have the same names.
+ .SH OPTIONS
+ The options can be divided into several different groups.
+ .SS COMMANDS
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre3.002.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre3.002.diff
new file mode 100644
index 0000000..175f9df
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre3.002.diff
@@ -0,0 +1,194 @@
+--- ebtables-v2.0pre3.001/ebtables.c	Fri Apr 19 22:07:46 2002
++++ ebtables-v2.0pre3.002/ebtables.c	Fri Apr 19 23:43:05 2002
+@@ -62,6 +62,8 @@
+ 	{ "policy"        , required_argument, 0, 'P' },
+ 	{ "in-interface"  , required_argument, 0, 'i' },
+ 	{ "in-if"         , required_argument, 0, 'i' },
++	{ "logical-in"    , required_argument, 0, 1   },
++	{ "logical-out"   , required_argument, 0, 2   },
+ 	{ "out-interface" , required_argument, 0, 'o' },
+ 	{ "out-if"        , required_argument, 0, 'o' },
+ 	{ "version"       , no_argument      , 0, 'V' },
+@@ -481,6 +483,16 @@
+ 				printf("! ");
+ 			printf("in-if: %s, ", hlp->in);
+ 		}
++		if (hlp->logical_in[0] != '\0') {
++			if (hlp->invflags & EBT_ILOGICALIN)
++				printf("! ");
++			printf("logical in-if: %s, ", hlp->logical_in);
++		}
++		if (hlp->logical_out[0] != '\0') {
++			if (hlp->invflags & EBT_ILOGICALOUT)
++				printf("! ");
++			printf("logical out-if: %s, ", hlp->logical_out);
++		}
+ 		if (hlp->out[0] != '\0') {
+ 			if (hlp->invflags & EBT_IOUT)
+ 				printf("! ");
+@@ -560,6 +572,8 @@
+ "--dst    -d [!] address       : destination mac address\n"
+ "--in-if  -i [!] name          : network input interface name\n"
+ "--out-if -o [!] name          : network output interface name\n"
++"--logical-in  [!] name        : logical bridge input interface name\n"
++"--logical-out [!] name        : logical bridge output interface name\n"
+ "--version -V                  : print package version\n"
+ "\n" ,
+ 	prog_name,
+@@ -1095,15 +1109,17 @@
+ 	*flags |= mask;
+ }
+ 
+-#define OPT_COMMAND  0x01
+-#define OPT_TABLE    0x02
+-#define OPT_IN       0x04
+-#define OPT_OUT      0x08
+-#define OPT_JUMP     0x10
+-#define OPT_PROTOCOL 0x20
+-#define OPT_SOURCE   0x40
+-#define OPT_DEST     0x80
+-#define OPT_ZERO     0x100
++#define OPT_COMMAND    0x01
++#define OPT_TABLE      0x02
++#define OPT_IN         0x04
++#define OPT_OUT        0x08
++#define OPT_JUMP       0x10
++#define OPT_PROTOCOL   0x20
++#define OPT_SOURCE     0x40
++#define OPT_DEST       0x80
++#define OPT_ZERO       0x100
++#define OPT_LOGICALIN  0x200
++#define OPT_LOGICALOUT 0x400
+ // the main thing
+ int main(int argc, char *argv[])
+ {
+@@ -1261,7 +1277,9 @@
+ 			break;
+ 
+ 		case 'i': // input interface
++		case 1  : // logical input interface
+ 		case 'o': // output interface
++		case 2  : // logical output interface
+ 		case 'j': // target
+ 		case 'p': // net family protocol
+ 		case 's': // source mac
+@@ -1287,6 +1305,23 @@
+ 				strcpy(new_entry->in, argv[optind - 1]);
+ 				break;
+ 			}
++			if (c == 1) {
++				check_option(&replace.flags, OPT_LOGICALIN);
++				if (replace.selected_hook > 2)
++					print_error("Use logical in-interface "
++					   "only in INPUT, FORWARD and "
++					   "PREROUTING chains");
++				if (check_inverse(optarg))
++					new_entry->invflags |= EBT_ILOGICALIN;
++
++				if (optind > argc)
++					print_error("No logical in-interface "
++					            "specified");
++				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
++					print_error("Illegal interfacelength");
++				strcpy(new_entry->logical_in, argv[optind - 1]);
++				break;
++			}
+ 			if (c == 'o') {
+ 				check_option(&replace.flags, OPT_OUT);
+ 				if (replace.selected_hook < 2)
+@@ -1304,6 +1339,26 @@
+ 					print_error("Illegal interface "
+ 					            "length");
+ 				strcpy(new_entry->out, argv[optind - 1]);
++				break;
++			}
++			if (c == 2) {
++				check_option(&replace.flags, OPT_LOGICALOUT);
++				if (replace.selected_hook < 2)
++					print_error("Use logical out-interface "
++					   "only in OUTPUT, FORWARD and "
++					   "POSTROUTING chains");
++				if (check_inverse(optarg))
++					new_entry->invflags |= EBT_ILOGICALOUT;
++
++				if (optind > argc)
++					print_error("No logical out-interface "
++					            "specified");
++
++				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
++					print_error("Illegal interface "
++					            "length");
++				strcpy(new_entry->logical_out,
++				   argv[optind - 1]);
+ 				break;
+ 			}
+ 			if (c == 'j') {
+--- ebtables-v2.0pre3.001/communication.c	Fri Apr 19 22:07:46 2002
++++ ebtables-v2.0pre3.002/communication.c	Fri Apr 19 22:57:13 2002
+@@ -115,6 +115,10 @@
+ 			tmp->ethproto = e->ethproto;
+ 			memcpy(tmp->in, e->in, sizeof(tmp->in));
+ 			memcpy(tmp->out, e->out, sizeof(tmp->out));
++			memcpy(tmp->logical_in, e->logical_in,
++			   sizeof(tmp->logical_in));
++			memcpy(tmp->logical_out, e->logical_out,
++			   sizeof(tmp->logical_out));
+ 			memcpy(tmp->sourcemac, e->sourcemac,
+ 			   sizeof(tmp->sourcemac));
+ 			memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
+@@ -298,6 +302,10 @@
+ 		new->ethproto = e->ethproto;
+ 		memcpy(new->in, e->in, sizeof(new->in));
+ 		memcpy(new->out, e->out, sizeof(new->out));
++		memcpy(new->logical_in, e->logical_in,
++		   sizeof(new->logical_in));
++		memcpy(new->logical_out, e->logical_out,
++		   sizeof(new->logical_out));
+ 		memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
+ 		memcpy(new->destmac, e->destmac, sizeof(new->destmac));
+ 		new->m_list = NULL;
+--- ebtables-v2.0pre3.001/ebtables.8	Fri Apr 19 22:07:46 2002
++++ ebtables-v2.0pre3.002/ebtables.8	Fri Apr 19 23:28:06 2002
+@@ -184,6 +184,14 @@
+ .B --in-if
+ is an alias for this option.
+ .TP
++.BR "--logical-in " "[!] \fIname\fP"
++The (logical) bridge interface via which a frame is received (for the
++.BR INPUT ,
++.B FORWARD
++and
++.B PREROUTING
++chains).
++.TP
+ .BR "-o, --out-interface " "[!] \fIname\fP"
+ The interface via which a frame is going to be sent (for the
+ .BR OUTPUT ,
+@@ -193,6 +201,15 @@
+ chains). The flag
+ .B --out-if
+ is an alias for this option.
++.TP
++.BR "--logical-out " "[!] \fIname\fP"
++The (logical) bridge interface via which a frame is going to be sent (for
++the
++.BR OUTPUT ,
++.B FORWARD
++and
++.B POSTROUTING
++chains).
+ .TP
+ .BR "-s, --source " "[!] \fIaddress\fP"
+ The source mac address. The flag
+--- ebtables-v2.0pre3.001/include/ebtables_u.h	Wed Apr 10 22:29:01 2002
++++ ebtables-v2.0pre3.002/include/ebtables_u.h	Fri Apr 19 22:55:15 2002
+@@ -82,7 +82,9 @@
+ 	__u32 invflags;
+ 	__u16 ethproto;
+ 	__u8 in[IFNAMSIZ];
++	__u8 logical_in[IFNAMSIZ];
+ 	__u8 out[IFNAMSIZ];
++	__u8 logical_out[IFNAMSIZ];
+ 	__u8 sourcemac[ETH_ALEN];
+ 	__u8 destmac[ETH_ALEN];
+ 	struct ebt_u_match_list *m_list;
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre3.003.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre3.003.diff
new file mode 100644
index 0000000..1a80a01
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre3.003.diff
@@ -0,0 +1,66 @@
+* Add brouter support
+* Give better advice when a table is not found by the kernel
+
+--- ebtables-v2.0pre3.002/Makefile	Sun Apr 14 15:03:11 2002
++++ ebtables-v2.0pre3.003/Makefile	Mon Apr 22 20:05:21 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre2 (April 2002)"
++PROGVERSION:="2.0pre3 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre3.002/ebtables.c	Mon Apr 22 20:08:03 2002
++++ ebtables-v2.0pre3.003/ebtables.c	Sat Apr 20 20:07:42 2002
+@@ -48,7 +48,8 @@
+ 	[NF_BR_LOCAL_IN]"INPUT",
+ 	[NF_BR_FORWARD]"FORWARD",
+ 	[NF_BR_LOCAL_OUT]"OUTPUT",
+-	[NF_BR_POST_ROUTING]"POSTROUTING"
++	[NF_BR_POST_ROUTING]"POSTROUTING",
++	[NF_BR_BROUTING]"BROUTING"
+ };
+ 
+ // default command line options
+--- ebtables-v2.0pre3.002/communication.c	Mon Apr 22 20:08:03 2002
++++ ebtables-v2.0pre3.003/communication.c	Sun Apr 21 15:38:38 2002
+@@ -375,7 +375,8 @@
+ 	strcpy(repl.name, u_repl->name);
+ 	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+ 		print_error("A kernel module needed by your command is probably"
+-		            " not loaded. Try insmod ebtables or the like");
++		            " not loaded. Try insmod ebtables or"
++		            " insmod ebtable_%s", repl.name);
+ 
+ 	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+ 		print_memory();
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0pre3.003/extensions/ebtable_broute.c	Sat Apr 20 20:12:26 2002
+@@ -0,0 +1,25 @@
++#include <stdio.h>
++#include <sys/socket.h>
++#include "../include/ebtables_u.h"
++
++
++static void print_help(char **hn)
++{
++	printf("Supported chain for the nat table:\n");
++	printf("%s\n",hn[NF_BR_BROUTING]);
++}
++
++static struct
++ebt_u_table table =
++{
++	"broute",
++	NULL,
++	print_help,
++	NULL
++};
++
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
++{
++	register_table(&table);
++}
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre3.004.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre3.004.diff
new file mode 100644
index 0000000..9a3bc67
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre3.004.diff
@@ -0,0 +1,483 @@
+--- ebtables-v2.0pre3.003/ebtables.c	Sat Apr 27 16:57:47 2002
++++ ebtables-v2.0pre3.004/ebtables.c	Wed Apr 24 19:47:02 2002
+@@ -63,8 +63,8 @@
+ 	{ "policy"        , required_argument, 0, 'P' },
+ 	{ "in-interface"  , required_argument, 0, 'i' },
+ 	{ "in-if"         , required_argument, 0, 'i' },
+-	{ "logical-in"    , required_argument, 0, 1   },
+-	{ "logical-out"   , required_argument, 0, 2   },
++	{ "logical-in"    , required_argument, 0, 2   },
++	{ "logical-out"   , required_argument, 0, 3   },
+ 	{ "out-interface" , required_argument, 0, 'o' },
+ 	{ "out-if"        , required_argument, 0, 'o' },
+ 	{ "version"       , no_argument      , 0, 'V' },
+@@ -155,6 +155,8 @@
+ 	e->ethproto = 0;
+ 	strcpy(e->in, "");
+ 	strcpy(e->out, "");
++	strcpy(e->logical_in, "");
++	strcpy(e->logical_out, "");
+ 	e->m_list = NULL;
+ 	e->w_list = NULL;
+ 	// the init function of the standard target should have put the verdict
+@@ -1278,9 +1280,9 @@
+ 			break;
+ 
+ 		case 'i': // input interface
+-		case 1  : // logical input interface
++		case 2  : // logical input interface
+ 		case 'o': // output interface
+-		case 2  : // logical output interface
++		case 3  : // logical output interface
+ 		case 'j': // target
+ 		case 'p': // net family protocol
+ 		case 's': // source mac
+@@ -1306,7 +1308,7 @@
+ 				strcpy(new_entry->in, argv[optind - 1]);
+ 				break;
+ 			}
+-			if (c == 1) {
++			if (c == 2) {
+ 				check_option(&replace.flags, OPT_LOGICALIN);
+ 				if (replace.selected_hook > 2)
+ 					print_error("Use logical in-interface "
+@@ -1342,7 +1344,7 @@
+ 				strcpy(new_entry->out, argv[optind - 1]);
+ 				break;
+ 			}
+-			if (c == 2) {
++			if (c == 3) {
+ 				check_option(&replace.flags, OPT_LOGICALOUT);
+ 				if (replace.selected_hook < 2)
+ 					print_error("Use logical out-interface "
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0pre3.004/extensions/ebt_redirect.c	Sat Apr 27 17:18:16 2002
+@@ -0,0 +1,109 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <getopt.h>
++#include "../include/ebtables_u.h"
++#include <linux/netfilter_bridge/ebt_redirect.h>
++
++extern char *standard_targets[NUM_STANDARD_TARGETS];
++
++#define REDIRECT_TARGET '1'
++static struct option opts[] =
++{
++	{ "redirect-target"    , required_argument, 0, REDIRECT_TARGET },
++	{ 0 }
++};
++
++static void print_help()
++{
++	printf(
++	"redirect option:\n"
++	" --redirect-target target   : ACCEPT, DROP or CONTINUE\n");
++}
++
++static void init(struct ebt_entry_target *target)
++{
++	struct ebt_redirect_info *redirectinfo =
++	   (struct ebt_redirect_info *)target->data;
++
++	redirectinfo->target = EBT_ACCEPT;
++	return;
++}
++
++
++#define OPT_REDIRECT_TARGET  0x01
++static int parse(int c, char **argv, int argc,
++   const struct ebt_u_entry *entry, unsigned int *flags,
++   struct ebt_entry_target **target)
++{
++	int i;
++	struct ebt_redirect_info *redirectinfo =
++	   (struct ebt_redirect_info *)(*target)->data;
++
++	switch (c) {
++	case REDIRECT_TARGET:
++		check_option(flags, OPT_REDIRECT_TARGET);
++		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
++			if (!strcmp(optarg, standard_targets[i])) {
++				redirectinfo->target = i;
++				break;
++			}
++		if (i == NUM_STANDARD_TARGETS)
++			print_error("Illegal --redirect-target target");
++		break;
++	default:
++		return 0;
++	}
++	return 1;
++}
++
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target, const char *name, unsigned int hook)
++{
++	if ( (hook != NF_BR_PRE_ROUTING || strcmp(name, "nat")) &&
++	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++		print_error("Wrong chain for redirect");
++}
++
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_target *target)
++{
++	struct ebt_redirect_info *redirectinfo =
++	   (struct ebt_redirect_info *)target->data;
++
++	printf("redirect");
++	printf(" --redirect-target %s", standard_targets[redirectinfo->target]);
++}
++
++static int compare(const struct ebt_entry_target *t1,
++   const struct ebt_entry_target *t2)
++{
++	struct ebt_redirect_info *redirectinfo1 =
++	   (struct ebt_redirect_info *)t1->data;
++	struct ebt_redirect_info *redirectinfo2 =
++	   (struct ebt_redirect_info *)t2->data;
++
++	return redirectinfo1->target == redirectinfo2->target;
++}
++
++static struct ebt_u_target redirect_target =
++{
++	EBT_REDIRECT_TARGET,
++	sizeof(struct ebt_redirect_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts,
++};
++
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
++{
++	register_target(&redirect_target);
++}
+--- ebtables-v2.0pre3.003/extensions/ebt_nat.c	Sat Apr 27 16:57:41 2002
++++ ebtables-v2.0pre3.004/extensions/ebt_nat.c	Sat Apr 27 17:16:19 2002
+@@ -8,54 +8,71 @@
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_nat.h>
+ 
++extern char *standard_targets[NUM_STANDARD_TARGETS];
++
+ int to_source_supplied, to_dest_supplied;
+ 
+ #define NAT_S '1'
+ #define NAT_D '1'
++#define NAT_S_TARGET '2'
++#define NAT_D_TARGET '2'
+ static struct option opts_s[] =
+ {
+ 	{ "to-source"     , required_argument, 0, NAT_S },
+ 	{ "to-src"        , required_argument, 0, NAT_S },
+-	{ 0 },
++	{ "snat-target"    , required_argument, 0, NAT_S_TARGET },
++	{ 0 }
+ };
+ 
+ static struct option opts_d[] =
+ {
+ 	{ "to-destination", required_argument, 0, NAT_D },
+ 	{ "to-dst"        , required_argument, 0, NAT_D },
++	{ "dnat-target"    , required_argument, 0, NAT_D_TARGET },
+ 	{ 0 }
+ };
+ 
+ static void print_help_s()
+ {
+ 	printf(
+-	"snat option:\n"
+-	" --to-src address       : MAC address to map source to\n");
++	"snat options:\n"
++	" --to-src address       : MAC address to map source to\n"
++	" --snat-target target   : ACCEPT, DROP or CONTINUE\n");
+ }
+ 
+ static void print_help_d()
+ {
+ 	printf(
+-	"dnat option:\n"
+-	" --to-dst address       : MAC address to map destination to\n");
++	"dnat options:\n"
++	" --to-dst address       : MAC address to map destination to\n"
++	" --dnat-target target   : ACCEPT, DROP or CONTINUE\n");
+ }
+ 
+ static void init_s(struct ebt_entry_target *target)
+ {
++	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
++
+ 	to_source_supplied = 0;
++	natinfo->target = EBT_ACCEPT;
+ 	return;
+ }
+ 
+ static void init_d(struct ebt_entry_target *target)
+ {
++	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
++
+ 	to_dest_supplied = 0;
++	natinfo->target = EBT_ACCEPT;
++	return;
+ }
+ 
+-#define OPT_SNAT  0x01
++#define OPT_SNAT         0x01
++#define OPT_SNAT_TARGET  0x02
+ static int parse_s(int c, char **argv, int argc,
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
++	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 
+ 	switch (c) {
+@@ -65,17 +82,29 @@
+ 		if (getmac(optarg, natinfo->mac))
+ 			print_error("Problem with specified to-source mac");
+ 		break;
++	case NAT_S_TARGET:
++		check_option(flags, OPT_SNAT_TARGET);
++		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
++			if (!strcmp(optarg, standard_targets[i])) {
++				natinfo->target = i;
++				break;
++			}
++		if (i == NUM_STANDARD_TARGETS)
++			print_error("Illegal --snat-target target");
++		break;
+ 	default:
+-	return 0;
++		return 0;
+ 	}
+ 	return 1;
+ }
+ 
+-#define OPT_DNAT  0x01
++#define OPT_DNAT        0x01
++#define OPT_DNAT_TARGET 0x02
+ static int parse_d(int c, char **argv, int argc,
+    const struct ebt_u_entry *entry, unsigned int *flags,
+    struct ebt_entry_target **target)
+ {
++	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+ 
+ 	switch (c) {
+@@ -86,8 +115,18 @@
+ 			print_error("Problem with specified "
+ 			            "to-destination mac");
+ 		break;
++	case NAT_D_TARGET:
++		check_option(flags, OPT_DNAT_TARGET);
++		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
++			if (!strcmp(optarg, standard_targets[i])) {
++				natinfo->target = i;
++				break;
++			}
++		if (i == NUM_STANDARD_TARGETS)
++			print_error("Illegal --dnat-target target");
++		break;
+ 	default:
+-	return 0;
++		return 0;
+ 	}
+ 	return 1;
+ }
+@@ -96,18 +135,18 @@
+    const struct ebt_entry_target *target, const char *name, unsigned int hook)
+ {
+ 	if (hook != NF_BR_POST_ROUTING || strcmp(name, "nat"))
+-		print_error("Wrong chain for SNAT");
++		print_error("Wrong chain for snat");
+ 	if (to_source_supplied == 0)
+ 		print_error("No snat address supplied");
+-
+ }
+ 
+ static void final_check_d(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target, const char *name, unsigned int hook)
+ {
+-	if ( (hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
+-	   strcmp(name, "nat") )
+-		print_error("Wrong chain for DNAT");
++	if ( ((hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
++	   strcmp(name, "nat")) &&
++	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++		print_error("Wrong chain for dnat");
+ 	if (to_dest_supplied == 0)
+ 		print_error("No dnat address supplied");
+ }
+@@ -122,6 +161,7 @@
+ 	for (i = 0; i < ETH_ALEN; i++)
+ 		printf("%02x%s",
+ 		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++	printf(" --snat-target %s", standard_targets[natinfo->target]);
+ }
+ 
+ static void print_d(const struct ebt_u_entry *entry,
+@@ -134,6 +174,7 @@
+ 	for (i = 0; i < ETH_ALEN; i++)
+ 		printf("%02x%s",
+ 		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++	printf(" --dnat-target %s", standard_targets[natinfo->target]);
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+@@ -142,13 +183,15 @@
+ 	struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data;
+ 	struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data;
+ 
+-	return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac));
++
++	return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac)) &&
++	   natinfo1->target == natinfo2->target;
+ }
+ 
+ static struct ebt_u_target snat_target =
+ {
+ 	EBT_SNAT_TARGET,
+-	sizeof(struct ebt_nat_info) + sizeof(struct ebt_entry_target),
++	sizeof(struct ebt_nat_info),
+ 	print_help_s,
+ 	init_s,
+ 	parse_s,
+--- ebtables-v2.0pre3.003/extensions/Makefile	Sat Apr  6 21:56:53 2002
++++ ebtables-v2.0pre3.004/extensions/Makefile	Tue Apr 23 22:46:21 2002
+@@ -1,7 +1,7 @@
+ #! /usr/bin/make
+ 
+-EXT_FUNC+=nat arp ip standard log
+-EXT_TABLES+=filter nat
++EXT_FUNC+=nat arp ip standard log redirect
++EXT_TABLES+=filter nat broute
+ EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+ EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+ 
+--- ebtables-v2.0pre3.003/ChangeLog	Sun Apr 14 14:15:59 2002
++++ ebtables-v2.0pre3.004/ChangeLog	Sat Apr 27 17:24:26 2002
+@@ -1,3 +1,9 @@
++20020427
++	* added broute table.
++	* added redirect target.
++	* added --redirect-target, --snat-target and --dnat-target options.
++	* added logical_out and logical_in
++	* snat bugfix (->size)
+ 20020414
+ 	* fixed some things in the manual.
+ 	* fixed -P problem.
+--- ebtables-v2.0pre3.003/ebtables.8	Sat Apr 27 16:57:44 2002
++++ ebtables-v2.0pre3.004/ebtables.8	Sat Apr 27 13:33:37 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "14 April 2002"
++.TH EBTABLES 8  "27 April 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -40,7 +40,7 @@
+ complicated. This man page is written with the man page of iptables
+ next to it, so don't be surprised to see copied sentences and structure.
+ 
+-There are two tables with each three built-in chains. Each chain is a list
++There are three tables with built-in chains. Each chain is a list
+ of rules which can match frames: each rule specifies what to do with a
+ frame which matches. This is called a 'target'. The tables are used to
+ divide functionality into different sets of chains.
+@@ -66,7 +66,7 @@
+ .B "TARGET EXTENSIONS"
+ section.
+ .SS TABLES
+-There are two tables.
++There are three tables.
+ .TP
+ .B "-t, --table"
+ This option specifies the frame matching table which the command should
+@@ -90,6 +90,22 @@
+ of chains POSTROUTING and PREROUTING: it would be more accurate to call them
+ PREFORWARDING and POSTFORWARDING, but for all those who come from the iptables
+ world to ebtables it is easier to have the same names.
++.BR broute ,
++this table is used to make a brouter, it has one chain:
++.BR BROUTING .
++The targets
++.BR DROP and ACCEPT
++have special meaning in this table.
++.B DROP
++actually means the frame has to be routed, while
++.B ACCEPT
++means the frame has to be bridged. The
++.B BROUTING
++chain is traversed very early. It is only traversed by frames entering on
++a bridge enslaved nic that is in forwarding state. Normally those frames
++would be bridged, but you can decide otherwise here. The
++.B redirect
++target is very handy here.
+ .SH OPTIONS
+ The options can be divided into several different groups.
+ .SS COMMANDS
+@@ -334,13 +350,21 @@
+ The flag
+ .B --to-src
+ is an alias for this option.
++.br
++.BR "--snat-target " "\fItarget\fP"
++.br
++Specifies the standard target. After doing the snat, the rule still has 
++to give a standard target so ebtables knows what to do.
++The default target is ACCEPT. Making it CONTINUE could let you use
++multiple target extensions on the same frame. Making it DROP doesn't
++make sense, but you could do that too.
+ .TP
+ .B dnat
+ The
+ .B dnat
+ target can only be used in the
+-.BR PREROUTING " and the
+-.BR OUTPUT " chains of the " nat " table."
++.BR BROUTING " chain of the " broute " table and the "
++.BR PREROUTING " and " OUTPUT " chains of the " nat " table."
+ It specifies that the destination mac address has to be changed.
+ .br
+ .BR "--to-destination " "\fIaddress\fP"
+@@ -348,6 +372,31 @@
+ The flag
+ .B --to-dst
+ is an alias for this option.
++.br
++.BR "--dnat-target " "\fItarget\fP"
++.br
++Specifies the standard target. After doing the dnat, the rule still has to
++give a standard target so ebtables knows what to do.
++The default target is ACCEPT. Making it CONTINUE could let you use 
++multiple target extensions on the same frame. Making it DROP only makes
++sense in the BROUTING chain but using the redirect target is more logical
++there.
++.TP
++.B redirect
++The
++.B redirect
++target will change the MAC target address to that of the physical nic the
++frame arrived on. This target can only be used in the
++.BR BROUTING " chain of the " broute " table and the "
++.BR PREROUTING " chain of the " nat " table."
++.br
++.BR "--redirect-target " "\fItarget\fP"
++.br
++Specifies the standard target. After doing the MAC redirect, the rule
++still has to give a standard target so ebtables knows what to do.
++The default target is ACCEPT. Making it CONTINUE could let you use 
++multiple target extensions on the same frame. Making it DROP in the
++BROUTING chain will let the frames be routed.
+ .SH FILES
+ .I /etc/etherproto
+ .SH BUGS
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre4.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre4.001.diff
new file mode 100644
index 0000000..c4a9e39
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre4.001.diff
@@ -0,0 +1,522 @@
+--- ebtables-v2.0pre3/Makefile	Sat Apr 27 22:31:19 2002
++++ ebtables-v2.0pre4.001/Makefile	Thu May  2 19:02:44 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre3 (April 2002)"
++PROGVERSION:="2.0pre4 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -32,12 +32,12 @@
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
+ 
+-/etc/etherproto: etherproto
++/etc/ethertypes: ethertypes
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
+ 
+ install: $(MANDIR)/man8/ebtables.8 headers \
+-	ebtables /etc/etherproto
++	ebtables /etc/ethertypes
+ 
+ clean:
+ 	-rm -f ebtables
+--- ebtables-v2.0pre3/ebtables.c	Sat Apr 27 22:31:21 2002
++++ ebtables-v2.0pre4.001/ebtables.c	Fri May  3 20:56:14 2002
+@@ -36,7 +36,7 @@
+ 
+ // here are the number-name correspondences kept for the ethernet
+ // frame type field
+-#define PROTOCOLFILE "/etc/etherproto"
++#define PROTOCOLFILE "/etc/ethertypes"
+ 
+ #define DATABASEHOOKNR NF_BR_NUMHOOKS
+ #define DATABASEHOOKNAME "DB"
+@@ -90,6 +90,13 @@
+ 	"CONTINUE",
+ };
+ 
++unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
++unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
++unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
++unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
++unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
++unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
++
+ // tells what happened to the old rules
+ static unsigned short *counterchanges;
+ // holds all the data
+@@ -466,20 +473,70 @@
+ 			}
+ 		}
+ 		if (hlp->bitmask & EBT_SOURCEMAC) {
++			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
++
+ 			printf("source mac: ");
+ 			if (hlp->invflags & EBT_ISOURCE)
+ 				printf("! ");
++			if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
++			    !memcmp(hlp->sourcemsk, msk_type_unicast, 6)) {
++				printf("Unicast");
++				goto endsrc;
++			}
++			if (!memcmp(hlp->sourcemac, mac_type_multicast, 6) &&
++			    !memcmp(hlp->sourcemsk, msk_type_multicast, 6)) {
++				printf("Multicast");
++				goto endsrc;
++			}
++			if (!memcmp(hlp->sourcemac, mac_type_broadcast, 6) &&
++			    !memcmp(hlp->sourcemsk, msk_type_broadcast, 6)) {
++				printf("Broadcast");
++				goto endsrc;
++			}
+ 			for (j = 0; j < ETH_ALEN; j++)
+ 				printf("%02x%s", hlp->sourcemac[j],
+-				   (j == ETH_ALEN - 1) ? ", " : ":");
++				   (j == ETH_ALEN - 1) ? "" : ":");
++			if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
++				printf("/");
++				for (j = 0; j < ETH_ALEN; j++)
++					printf("%02x%s", hlp->sourcemsk[j],
++					   (j == ETH_ALEN - 1) ? "" : ":");
++			}
++endsrc:
++			printf(", ");
+ 		}
+ 		if (hlp->bitmask & EBT_DESTMAC) {
++			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
++
+ 			printf("dest mac: ");
+ 			if (hlp->invflags & EBT_IDEST)
+ 				printf("! ");
++			if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
++			    !memcmp(hlp->destmsk, msk_type_unicast, 6)) {
++				printf("Unicast");
++				goto enddst;
++			}
++			if (!memcmp(hlp->destmac, mac_type_multicast, 6) &&
++			    !memcmp(hlp->destmsk, msk_type_multicast, 6)) {
++				printf("Multicast");
++				goto enddst;
++			}
++			if (!memcmp(hlp->destmac, mac_type_broadcast, 6) &&
++			    !memcmp(hlp->destmsk, msk_type_broadcast, 6)) {
++				printf("Broadcast");
++				goto enddst;
++			}
+ 			for (j = 0; j < ETH_ALEN; j++)
+ 				printf("%02x%s", hlp->destmac[j],
+-				   (j == ETH_ALEN - 1) ? ", " : ":");
++				   (j == ETH_ALEN - 1) ? "" : ":");
++			if (memcmp(hlp->destmsk, hlpmsk, 6)) {
++				printf("/");
++				for (j = 0; j < ETH_ALEN; j++)
++					printf("%02x%s", hlp->destmsk[j],
++					   (j == ETH_ALEN - 1) ? "" : ":");
++			}
++enddst:
++			printf(", ");
+ 		}
+ 		if (hlp->in[0] != '\0') {
+ 			if (hlp->invflags & EBT_IIN)
+@@ -1096,6 +1153,39 @@
+ 	return 0;
+ }
+ 
++int getmac_and_mask(char *from, char *to, char *mask)
++{
++	char *p;
++	int i;
++
++	if (strcasecmp(from, "Unicast") == 0) {
++		memcpy(to, mac_type_unicast, ETH_ALEN);
++		memcpy(mask, msk_type_unicast, ETH_ALEN);
++		return 0;
++	}
++	if (strcasecmp(from, "Multicast") == 0) {
++		memcpy(to, mac_type_multicast, ETH_ALEN);
++		memcpy(mask, msk_type_multicast, ETH_ALEN);
++		return 0;
++	}
++	if (strcasecmp(from, "Broadcast") == 0) {
++		memcpy(to, mac_type_broadcast, ETH_ALEN);
++		memcpy(mask, msk_type_broadcast, ETH_ALEN);
++		return 0;
++	}
++	if ( (p = strrchr(from, '/')) != NULL) {
++		*p = '\0';
++		if (getmac(p + 1, mask))
++			return -1;
++	} else
++		memset(mask, 0xff, ETH_ALEN);
++	if (getmac(from, to))
++		return -1;
++	for (i = 0; i < ETH_ALEN; i++)
++		to[i] &= mask[i];
++	return 0;
++}
++
+ int check_inverse(const char option[])
+ {
+ 	if (strcmp(option, "!") == 0) {
+@@ -1294,9 +1384,11 @@
+ 				print_error("Command and option do not match");
+ 			if (c == 'i') {
+ 				check_option(&replace.flags, OPT_IN);
+-				if (replace.selected_hook > 2)
++				if (replace.selected_hook > 2 &&
++				   replace.selected_hook < NF_BR_BROUTING)
+ 					print_error("Use in-interface only in "
+-					"INPUT, FORWARD and PREROUTING chains");
++					   "INPUT, FORWARD, PREROUTING and"
++					   "BROUTING chains");
+ 				if (check_inverse(optarg))
+ 					new_entry->invflags |= EBT_IIN;
+ 
+@@ -1310,10 +1402,11 @@
+ 			}
+ 			if (c == 2) {
+ 				check_option(&replace.flags, OPT_LOGICALIN);
+-				if (replace.selected_hook > 2)
++				if (replace.selected_hook > 2 &&
++				   replace.selected_hook < NF_BR_BROUTING)
+ 					print_error("Use logical in-interface "
+-					   "only in INPUT, FORWARD and "
+-					   "PREROUTING chains");
++					   "only in INPUT, FORWARD, "
++					   "PREROUTING and BROUTING chains");
+ 				if (check_inverse(optarg))
+ 					new_entry->invflags |= EBT_ILOGICALIN;
+ 
+@@ -1398,8 +1491,8 @@
+ 				if (optind > argc)
+ 					print_error("No source mac "
+ 					            "specified");
+-				if (getmac(argv[optind - 1],
+-				   new_entry->sourcemac))
++				if (getmac_and_mask(argv[optind - 1],
++				   new_entry->sourcemac, new_entry->sourcemsk))
+ 					print_error("Problem with specified "
+ 					            "source mac");
+ 				new_entry->bitmask |= EBT_SOURCEMAC;
+@@ -1413,8 +1506,8 @@
+ 				if (optind > argc)
+ 					print_error("No destination mac "
+ 					            "specified");
+-				if (getmac(argv[optind - 1],
+-				   new_entry->destmac))
++				if (getmac_and_mask(argv[optind - 1],
++				   new_entry->destmac, new_entry->destmsk))
+ 					print_error("Problem with specified "
+ 					            "destination mac");
+ 				new_entry->bitmask |= EBT_DESTMAC;
+--- ebtables-v2.0pre3/communication.c	Sat Apr 27 22:31:19 2002
++++ ebtables-v2.0pre4.001/communication.c	Thu May  2 19:02:44 2002
+@@ -121,7 +121,10 @@
+ 			   sizeof(tmp->logical_out));
+ 			memcpy(tmp->sourcemac, e->sourcemac,
+ 			   sizeof(tmp->sourcemac));
++			memcpy(tmp->sourcemsk, e->sourcemsk,
++			   sizeof(tmp->sourcemsk));
+ 			memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
++			memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
+ 
+ 			base = p;
+ 			p += sizeof(struct ebt_entry);
+@@ -307,7 +310,9 @@
+ 		memcpy(new->logical_out, e->logical_out,
+ 		   sizeof(new->logical_out));
+ 		memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
++		memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
+ 		memcpy(new->destmac, e->destmac, sizeof(new->destmac));
++		memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
+ 		new->m_list = NULL;
+ 		new->w_list = NULL;
+ 		new->next = NULL;
+--- ebtables-v2.0pre3/ChangeLog	Sat Apr 27 22:31:21 2002
++++ ebtables-v2.0pre4.001/ChangeLog	Thu May  2 19:02:44 2002
+@@ -1,3 +1,11 @@
++20020501
++	* allow -i and --logical-in in BROUTING
++	* update the manual page
++	* rename /etc/etherproto into /etc/ethertypes (seems to be a more
++	  standard name)
++	* add MAC mask for -s and -d, also added Unicast, Multicast and
++	  Broadcast specification for specifying a (family of) MAC
++	  addresses.
+ 20020427
+ 	* added broute table.
+ 	* added redirect target.
+--- ebtables-v2.0pre3/ebtables.8	Sat Apr 27 22:31:21 2002
++++ ebtables-v2.0pre4.001/ebtables.8	Thu May  2 19:02:44 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "27 April 2002"
++.TH EBTABLES 8  "01 May 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -31,7 +31,7 @@
+ .br
+ .B "ebtables -L DB"
+ .br
+-.BR "ebtables -[cb] [" "y/n" "]"
++.BR "ebtables -[b] [" "y/n" "]"
+ .br
+ .SH DESCRIPTION
+ .B ebtables
+@@ -88,13 +88,14 @@
+ .B POSTROUTING
+ (for altering frames as they are about to go out). A small note on the naming
+ of chains POSTROUTING and PREROUTING: it would be more accurate to call them
+-PREFORWARDING and POSTFORWARDING, but for all those who come from the iptables
+-world to ebtables it is easier to have the same names.
++PREFORWARDING and POSTFORWARDING, but for all those who come from the
++.BR iptables " world to " ebtables
++it is easier to have the same names.
+ .BR broute ,
+ this table is used to make a brouter, it has one chain:
+ .BR BROUTING .
+ The targets
+-.BR DROP and ACCEPT
++.BR DROP " and " ACCEPT
+ have special meaning in this table.
+ .B DROP
+ actually means the frame has to be routed, while
+@@ -178,8 +179,9 @@
+ .B ebtables
+ for these frames is
+ .BR LENGTH .
++.br
+ The file
+-.B /etc/etherproto
++.B /etc/ethertypes
+ can be used to show readable
+ characters instead of hexadecimal numbers for the protocols. For example,
+ .I 0x0800
+@@ -193,9 +195,8 @@
+ .BR "-i, --in-interface " "[!] \fIname\fP"
+ The interface via which a frame is received (for the
+ .BR INPUT ,
+-.B FORWARD
+-and
+-.B PREROUTING
++.BR FORWARD ,
++.BR PREROUTING " and " BROUTING
+ chains). The flag
+ .B --in-if
+ is an alias for this option.
+@@ -203,9 +204,8 @@
+ .BR "--logical-in " "[!] \fIname\fP"
+ The (logical) bridge interface via which a frame is received (for the
+ .BR INPUT ,
+-.B FORWARD
+-and
+-.B PREROUTING
++.BR FORWARD ,
++.BR PREROUTING " and " BROUTING
+ chains).
+ .TP
+ .BR "-o, --out-interface " "[!] \fIname\fP"
+@@ -227,23 +227,31 @@
+ .B POSTROUTING
+ chains).
+ .TP
+-.BR "-s, --source " "[!] \fIaddress\fP"
+-The source mac address. The flag
++.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
++The source mac address. Both mask and address are written as 6 hexadecimal
++numbers seperated by colons. Alternatively one can specify Unicast,
++Multicast or Broadcast.
++.br
++Unicast=00:00:00:00:00:00/01:00:00:00:00:00,
++Multicast=01:00:00:00:00:00/01:00:00:00:00:00 and
++Broadcast=ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff. Note that a broadcast
++address will also match the multicast specification. The flag
+ .B --src
+ is an alias for this option.
+ .TP
+-.BR "-d, --destination " "[!] \fIaddress\fP"
+-The destination mac address. The flag
++.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
++The destination mac address. See -s (above) for more details. The flag
+ .B --dst
+ is an alias for this option.
+ 
+ .SS OTHER OPTIONS
++.TP
+ .B "-V, --version"
+ Show the version of the userprogram.
+ .TP
+ .B "-h, --help"
+-Give a brief description of the command syntax. Here you can specify names of
+-extensions and
++Give a brief description of the command syntax. Here you can also specify
++names of extensions and
+ .B ebtables
+ will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
+ .TP
+@@ -258,7 +266,8 @@
+ or a target extension, see
+ .BR "TARGET EXTENSIONS" .
+ .SH MATCH EXTENSIONS
+-ebtables extensions are precompiled into the userspace tool. So there is no need
++.B ebtables
++extensions are precompiled into the userspace tool. So there is no need
+ to explicitly load them with a -m option like in iptables. However, these
+ extensions deal with functionality supported by supplemental kernel modules.
+ .SS ip
+@@ -291,10 +300,11 @@
+ .BR ARP " or " RARP .
+ .TP
+ .BR "--arp-opcode " "[!] \fIopcode\fP"
+-The (r)arp opcode (decimal or a string, see ebtables help).
++The (r)arp opcode (decimal or a string, for more details see ebtables -h arp).
+ .TP
+ .BR "--arp-htype " "[!] \fIhardware type\fP"
+-The hardware type (decimal or the string "Ethernet"). This is normally Ethernet (value 1).
++The hardware type, this can be a decimal or the string "Ethernet". This
++is normally Ethernet (value 1).
+ .TP
+ .BR "--arp-ptype " "[!] \fIprotocol type\fP"
+ The protocol type for which the (r)arp is used (hexadecimal or the string "IPv4").
+@@ -354,7 +364,9 @@
+ .BR "--snat-target " "\fItarget\fP"
+ .br
+ Specifies the standard target. After doing the snat, the rule still has 
+-to give a standard target so ebtables knows what to do.
++to give a standard target so
++.B ebtables
++knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP doesn't
+ make sense, but you could do that too.
+@@ -376,7 +388,9 @@
+ .BR "--dnat-target " "\fItarget\fP"
+ .br
+ Specifies the standard target. After doing the dnat, the rule still has to
+-give a standard target so ebtables knows what to do.
++give a standard target so
++.B ebtables
++knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use 
+ multiple target extensions on the same frame. Making it DROP only makes
+ sense in the BROUTING chain but using the redirect target is more logical
+@@ -385,7 +399,7 @@
+ .B redirect
+ The
+ .B redirect
+-target will change the MAC target address to that of the physical nic the
++target will change the MAC target address to that of the bridge device the
+ frame arrived on. This target can only be used in the
+ .BR BROUTING " chain of the " broute " table and the "
+ .BR PREROUTING " chain of the " nat " table."
+@@ -393,12 +407,14 @@
+ .BR "--redirect-target " "\fItarget\fP"
+ .br
+ Specifies the standard target. After doing the MAC redirect, the rule
+-still has to give a standard target so ebtables knows what to do.
++still has to give a standard target so
++.B ebtables
++knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use 
+ multiple target extensions on the same frame. Making it DROP in the
+ BROUTING chain will let the frames be routed.
+ .SH FILES
+-.I /etc/etherproto
++.I /etc/ethertypes
+ .SH BUGS
+ This won't work on an architecture with a user32/kernel64 situation like the Sparc64.
+ .SH AUTHOR
+--- ebtables-v2.0pre3/etherproto	Wed Apr  3 13:16:59 2002
++++ /dev/null	Thu Aug 24 11:00:32 2000
+@@ -1,36 +0,0 @@
+-  # all whitespace is ignored
+-  # comment lines must have a '#' as the first character
+-  # all protocol numbers are in hexadecimal form
+-	# maximum namesize = 20 characters
+-	# always put tabs or spaces between the name and the protocol number
+-# don't use more than 4 digits for the protocol number
+-# programs using this file should not be case sensitive
+-# that's all :-))
+-IPV4 	0800	put your comments behind, on the same line, after a tab
+-X25	0800    or whitespace
+-ARP	0806
+-IPX	8137
+-IPV6	86DD
+-NetBEUI	8191
+-
+-# some definitions from the kernel (/include/linux/if_ether.h)
+-
+-BPQ	08FF	G8BPQ AX.25 Ethernet Packet
+-DEC	6000	DEC Assigned proto
+-DNA_DL	6001	DEC DNA Dump/Load
+-DNA_RC	6002	DEC DNA Remote Console
+-DNA_RT	6003	DEC DNA Routing
+-LAT	6004	DEC LAT
+-DIAG	6005	DEC Diagnostics
+-CUST	6006	DEC Customer use
+-SCA	6007	DEC Systems Comms Arch
+-RARP	8035	Reverse Addr Res packet
+-ATALK	809B	Appletalk DDP
+-AARP	80F3	Appletalk AARP
+-IPX	8137	IPX over DIX
+-PPP_DISC	8863	PPPoE discovery messages
+-PPP_SES	8864	PPPoE session messages
+-ATMMPOA	884C	MultiProtocol over ATM
+-ATMFATE	8884	Frame-based ATM Transport over Ethernet
+-
+-
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0pre4.001/ethertypes	Thu May  2 19:02:44 2002
+@@ -0,0 +1,36 @@
++  # all whitespace is ignored
++  # comment lines must have a '#' as the first character
++  # all protocol numbers are in hexadecimal form
++	# maximum namesize = 20 characters
++	# always put tabs or spaces between the name and the protocol number
++# don't use more than 4 digits for the protocol number
++# programs using this file should not be case sensitive
++# that's all :-))
++IPV4 	0800	put your comments behind, on the same line, after a tab
++X25	0800    or whitespace
++ARP	0806
++IPX	8137
++IPV6	86DD
++NetBEUI	8191
++
++# some definitions from the kernel (/include/linux/if_ether.h)
++
++BPQ	08FF	G8BPQ AX.25 Ethernet Packet
++DEC	6000	DEC Assigned proto
++DNA_DL	6001	DEC DNA Dump/Load
++DNA_RC	6002	DEC DNA Remote Console
++DNA_RT	6003	DEC DNA Routing
++LAT	6004	DEC LAT
++DIAG	6005	DEC Diagnostics
++CUST	6006	DEC Customer use
++SCA	6007	DEC Systems Comms Arch
++RARP	8035	Reverse Addr Res packet
++ATALK	809B	Appletalk DDP
++AARP	80F3	Appletalk AARP
++IPX	8137	IPX over DIX
++PPP_DISC	8863	PPPoE discovery messages
++PPP_SES	8864	PPPoE session messages
++ATMMPOA	884C	MultiProtocol over ATM
++ATMFATE	8884	Frame-based ATM Transport over Ethernet
++
++
+--- ebtables-v2.0pre3/include/ebtables_u.h	Sat Apr 27 22:31:16 2002
++++ ebtables-v2.0pre4.001/include/ebtables_u.h	Thu May  2 19:02:44 2002
+@@ -86,7 +86,9 @@
+ 	__u8 out[IFNAMSIZ];
+ 	__u8 logical_out[IFNAMSIZ];
+ 	__u8 sourcemac[ETH_ALEN];
++	__u8 sourcemsk[ETH_ALEN];
+ 	__u8 destmac[ETH_ALEN];
++	__u8 destmsk[ETH_ALEN];
+ 	struct ebt_u_match_list *m_list;
+ 	struct ebt_u_watcher_list *w_list;
+ 	struct ebt_entry_target *t;
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre5.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre5.001.diff
new file mode 100644
index 0000000..42202a4
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre5.001.diff
@@ -0,0 +1,50 @@
+update help and VLAN ethertype in /etc/ethertypes
+
+--- ebtables-v2.0pre4/Makefile	Fri May  3 21:08:24 2002
++++ ebtables-v2.0pre5.001/Makefile	Fri May  3 21:34:23 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre4 (April 2002)"
++PROGVERSION:="2.0pre5 (April 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre4/ebtables.c	Fri May  3 21:08:24 2002
++++ ebtables-v2.0pre5.001/ebtables.c	Fri May  3 21:33:55 2002
+@@ -628,8 +628,8 @@
+ "--policy -P chain target      : Change policy on chain to target\n"
+ "Options:\n"
+ "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+-"--src    -s [!] address       : source mac address\n"
+-"--dst    -d [!] address       : destination mac address\n"
++"--src    -s [!] address[/mask]: source mac address\n"
++"--dst    -d [!] address[/mask]: destination mac address\n"
+ "--in-if  -i [!] name          : network input interface name\n"
+ "--out-if -o [!] name          : network output interface name\n"
+ "--logical-in  [!] name        : logical bridge input interface name\n"
+--- ebtables-v2.0pre4/ChangeLog	Fri May  3 21:08:25 2002
++++ ebtables-v2.0pre5.001/ChangeLog	Sat May 18 10:30:44 2002
+@@ -1,3 +1,5 @@
++	* update help for -s and -d
++	* add VLAN in ethertypes
+ 20020501
+ 	* allow -i and --logical-in in BROUTING
+ 	* update the manual page
+--- ebtables-v2.0pre4/ethertypes	Fri May  3 21:08:25 2002
++++ ebtables-v2.0pre5.001/ethertypes	Sat May 18 10:30:10 2002
+@@ -9,12 +9,10 @@
+ IPV4 	0800	put your comments behind, on the same line, after a tab
+ X25	0800    or whitespace
+ ARP	0806
++VLAN	8100
+ IPX	8137
+ IPV6	86DD
+ NetBEUI	8191
+-
+-# some definitions from the kernel (/include/linux/if_ether.h)
+-
+ BPQ	08FF	G8BPQ AX.25 Ethernet Packet
+ DEC	6000	DEC Assigned proto
+ DNA_DL	6001	DEC DNA Dump/Load
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre5.002.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre5.002.diff
new file mode 100644
index 0000000..5b2d037
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre5.002.diff
@@ -0,0 +1,61 @@
+--- ebtables-v2.0pre5.001/Makefile	Mon May 20 13:59:48 2002
++++ ebtables-v2.0pre5.002/Makefile	Mon May 20 14:06:38 2002
+@@ -2,12 +2,19 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre5 (April 2002)"
++PROGVERSION:="2.0pre5 (May 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+ include extensions/Makefile
+ 
++# Some kernel testers prefer to use a symlink for /usr/include/linux
++ifeq ($(SYMLINK), y)
++KERNEL_INCLUDES=symlink
++else
++KERNEL_INCLUDES=headers
++endif
++
+ .PHONY: headers
+ headers:
+ 	mkdir -p /usr/include/linux/netfilter_bridge
+@@ -18,6 +25,10 @@
+ 	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \
+ 		/usr/include/linux/netfilter_bridge.h
+ 
++.PHONY: symlink
++symlink:
++	ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux
++
+ communication.o: communication.c include/ebtables_u.h
+ 	$(CC) $(CFLAGS) -c -o $@ $<
+ 
+@@ -36,7 +47,7 @@
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
+ 
+-install: $(MANDIR)/man8/ebtables.8 headers \
++install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
+ 	ebtables /etc/ethertypes
+ 
+ clean:
+--- ebtables-v2.0pre5.001/THANKS	Sat Apr 13 17:40:35 2002
++++ ebtables-v2.0pre5.002/THANKS	Mon May 20 13:49:50 2002
+@@ -6,4 +6,4 @@
+ Jason Lunz
+ Tim Gardner
+ Loïc Minier
+-
++Nick Fedchik
+--- ebtables-v2.0pre5.001/ChangeLog	Mon May 20 13:59:48 2002
++++ ebtables-v2.0pre5.002/ChangeLog	Mon May 20 13:49:27 2002
+@@ -1,5 +1,7 @@
++20020520
+ 	* update help for -s and -d
+ 	* add VLAN in ethertypes
++	* add SYMLINK option for compiling
+ 20020501
+ 	* allow -i and --logical-in in BROUTING
+ 	* update the manual page
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre6.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre6.001.diff
new file mode 100644
index 0000000..a143d62
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre6.001.diff
@@ -0,0 +1,314 @@
+--- ebtables-v2.0pre5/Makefile	Mon May 20 14:06:38 2002
++++ ebtables-v2.0pre6.001/Makefile	Thu May 30 18:39:04 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre5 (May 2002)"
++PROGVERSION:="2.0pre6 (May 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -15,6 +15,8 @@
+ KERNEL_INCLUDES=headers
+ endif
+ 
++all:	ebtables
++
+ .PHONY: headers
+ headers:
+ 	mkdir -p /usr/include/linux/netfilter_bridge
+@@ -52,3 +54,5 @@
+ 
+ clean:
+ 	-rm -f ebtables
++	rm -f *.o *.c~
++	rm -f extensions/*.o extensions/*.c~
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0pre6.001/extensions/ebt_vlan.c	Thu May 30 18:38:44 2002
+@@ -0,0 +1,231 @@
++/*
++ * Summary: ebt_vlan userspace module
++ * 
++ * Description: 802.1Q Virtual LAN match support module for ebtables project.
++ * Enable to match 802.1Q VLAN tagged frames by VLAN numeric 
++ * identifier (12-bites field) and frame priority (3-bites field)
++ * 
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Nick Fedchik <nick@fedchik.org.ua>
++ *  
++ *  May, 2002
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <getopt.h>
++#include "../include/ebtables_u.h"
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++#define VLAN_ID    '1'
++#define VLAN_PRIO  '2'
++
++static struct option opts[] = {
++	{"vlan-id", required_argument, 0, VLAN_ID},
++	{"vlan-prio", required_argument, 0, VLAN_PRIO},
++	{0}
++};
++
++/*
++ * Print out help for ebtables -h vlan 
++ */
++static void print_help ()
++{
++	printf ("802.1Q VLAN options:\n"
++		"--vlan-id [!] id        : VLAN ID 1-4095 (integer)\n"
++		"--vlan-prio [!] prio    : VLAN Priority 0-7 (integer)\n");
++}
++
++/*
++ * Initialization function 
++ */
++static void init (struct ebt_entry_match *match)
++{
++	struct ebt_vlan_info *vlaninfo =
++	    (struct ebt_vlan_info *) match->data;
++	/*
++	 * Just clean initial values 
++	 */
++	vlaninfo->id = 0;
++	vlaninfo->prio = 0;
++	vlaninfo->invflags = 0;
++	vlaninfo->bitmask = 0;
++}
++
++#define OPT_VLAN_ID     0x01
++#define OPT_VLAN_PRIO   0x02
++static int
++parse (int c, char **argv, int argc,
++       const struct ebt_u_entry *entry, unsigned int *flags,
++       struct ebt_entry_match **match)
++{
++	struct ebt_vlan_info *vlaninfo =
++	    (struct ebt_vlan_info *) (*match)->data;
++	unsigned short i;
++	char *end;
++
++	switch (c) {
++	case VLAN_ID:
++		check_option (flags, OPT_VLAN_ID);
++		/*
++		 * Check If we got inversed arg for VID,
++		 * otherwise unset inversion flag 
++		 */
++		if (check_inverse (optarg))
++			vlaninfo->invflags |= EBT_VLAN_ID;
++		/*
++		 * Check arg value presense 
++		 */
++		if (optind > argc)
++			print_error ("Missing VLAN ID argument\n");
++		/*
++		 * Convert argv to long int,
++		 * set *end to end of argv string, 
++		 * base set 10 for decimal only 
++		 */
++		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
++		/*
++		 * Check arg val range 
++		 */
++		if (i < 1 || i >= 4096 || *end != '\0') {
++			i = 0;
++			print_error
++			    ("Problem with specified VLAN ID range\n");
++		}
++		vlaninfo->id = i;
++		vlaninfo->bitmask|=EBT_VLAN_ID;
++		break;
++
++	case VLAN_PRIO:
++		check_option (flags, OPT_VLAN_PRIO);
++		if (check_inverse (optarg))
++			vlaninfo->invflags |= EBT_VLAN_PRIO;
++		if (optind > argc)
++			print_error
++			    ("Missing VLAN Priority level argument\n");
++		/*
++		 * Convert argv to long int,
++		 * set *end to end of argv string, 
++		 * base set 10 for decimal only 
++		 */
++		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
++		/*
++		 * Check arg val range 
++		 */
++		if (i >= 8 || *end != '\0') {
++			i = 0;
++			print_error
++			    ("Problem with specified VLAN Priority range\n");
++		}
++		vlaninfo->prio = i;
++		vlaninfo->bitmask|=EBT_VLAN_PRIO;
++		break;
++
++	default:
++		return 0;
++	}
++	return 1;
++}
++
++/*
++ * Final check 
++ */
++static void
++final_check (const struct ebt_u_entry *entry,
++	     const struct ebt_entry_match *match,
++	     const char *name, unsigned int hook)
++{
++	/*
++	 * Is any proto supplied there? Or specified proto isn't 802.1Q?
++	 */
++	if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
++		print_error
++		    ("For matching 802.1Q VLAN the protocol must be specified as 802_1Q\n");
++}
++
++/*
++ * Print line when listing rules by ebtables -L 
++ */
++static void
++print (const struct ebt_u_entry *entry,
++       const struct ebt_entry_match *match)
++{
++	struct ebt_vlan_info *vlaninfo =
++	    (struct ebt_vlan_info *) match->data;
++
++	/*
++	 * Print VLAN ID if they are specified 
++	 */
++	if (vlaninfo->bitmask & EBT_VLAN_ID) {
++		printf ("vlan id: %s%d, ",
++			vlaninfo->invflags & EBT_VLAN_ID ? "!" : "",
++			vlaninfo->id);
++	}
++	/*
++	 * Print VLAN priority if they are specified 
++	 */
++	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
++		printf ("vlan prio: %s%d, ",
++			vlaninfo->invflags & EBT_VLAN_PRIO ? "!" : "",
++			vlaninfo->prio);
++	}
++}
++
++
++static int
++compare (const struct ebt_entry_match *vlan1,
++	 const struct ebt_entry_match *vlan2)
++{
++	struct ebt_vlan_info *vlaninfo1 =
++	    (struct ebt_vlan_info *) vlan1->data;
++	struct ebt_vlan_info *vlaninfo2 =
++	    (struct ebt_vlan_info *) vlan2->data;
++	/*
++	 * Compare argc 
++	 */
++	if (vlaninfo1->bitmask != vlaninfo2->bitmask)
++		return 0;
++	/*
++	 * Compare inv flags  
++	 */
++	if (vlaninfo1->invflags != vlaninfo2->invflags)
++		return 0;
++	/*
++	 * Compare VLAN ID if they are present 
++	 */
++	if (vlaninfo1->bitmask & EBT_VLAN_ID) {
++		if (vlaninfo1->id != vlaninfo2->id)
++			return 0;
++	};
++	/*
++	 * Compare VLAN Prio if they are present 
++	 */
++	if (vlaninfo1->bitmask & EBT_VLAN_PRIO) {
++		if (vlaninfo1->prio != vlaninfo2->prio)
++			return 0;
++	};
++	return 1;
++}
++
++static struct ebt_u_match vlan_match = {
++	EBT_VLAN_MATCH,
++	sizeof (struct ebt_vlan_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts,
++};
++
++static void _init (void) __attribute__ ((constructor));
++static void _init (void)
++{
++	register_match (&vlan_match);
++}
+--- ebtables-v2.0pre5/extensions/Makefile	Sat Apr 27 22:31:21 2002
++++ ebtables-v2.0pre6.001/extensions/Makefile	Thu May 30 18:38:44 2002
+@@ -1,6 +1,6 @@
+ #! /usr/bin/make
+ 
+-EXT_FUNC+=nat arp ip standard log redirect
++EXT_FUNC+=nat arp ip standard log redirect vlan
+ EXT_TABLES+=filter nat broute
+ EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+ EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+--- ebtables-v2.0pre5/ebtables.8	Fri May  3 21:08:25 2002
++++ ebtables-v2.0pre6.001/ebtables.8	Thu May 30 18:43:19 2002
+@@ -315,6 +315,17 @@
+ .TP
+ .BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+ The ARP IP destination address specification.
++.SS vlan
++Specify 802.1Q VLAN specific fields. These will only work if the protocol equals
++.BR 802_1Q .
++For more details see
++.BR "ebtables -h vlan" .
++.TP
++.BR "--vlan-id " "[!] \fIid\fP"
++The VLAN identifier (decimal number from 0 to 4095).
++.TP
++.BR "--vlan-prio " "[!] \fIprio\fP"
++The VLAN priority type, this can be a decimal number from 0 to 7. The default value is 0.
+ .SH WATCHER EXTENSION(S)
+ Watchers are things that only look at frames passing by. These watchers only see the
+ frame if the frame passes all the matches of the rule.
+--- ebtables-v2.0pre5/ethertypes	Sat May 18 10:30:10 2002
++++ ebtables-v2.0pre6.001/ethertypes	Thu May 30 18:38:44 2002
+@@ -1,15 +1,15 @@
+-  # all whitespace is ignored
+-  # comment lines must have a '#' as the first character
+-  # all protocol numbers are in hexadecimal form
+-	# maximum namesize = 20 characters
+-	# always put tabs or spaces between the name and the protocol number
++# all whitespace is ignored
++# comment lines must have a '#' as the first character
++# all protocol numbers are in hexadecimal form
++# maximum namesize = 20 characters
++# always put tabs or spaces between the name and the protocol number
+ # don't use more than 4 digits for the protocol number
+ # programs using this file should not be case sensitive
+ # that's all :-))
+ IPV4 	0800	put your comments behind, on the same line, after a tab
+ X25	0800    or whitespace
+ ARP	0806
+-VLAN	8100
++802_1Q	8100	802.1Q Virtual LAN tagged frame
+ IPX	8137
+ IPV6	86DD
+ NetBEUI	8191
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre7.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre7.001.diff
new file mode 100644
index 0000000..697b1f9
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre7.001.diff
@@ -0,0 +1,263 @@
+--- ebtables-v2.0pre6/Makefile	Thu May 30 18:39:04 2002
++++ ebtables-v2.0pre7.001/Makefile	Thu Jun  6 19:18:29 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre6 (May 2002)"
++PROGVERSION:="2.0pre7 (June 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -29,6 +29,7 @@
+ 
+ .PHONY: symlink
+ symlink:
++	rm -f /usr/include/linux
+ 	ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux
+ 
+ communication.o: communication.c include/ebtables_u.h
+@@ -53,6 +54,6 @@
+ 	ebtables /etc/ethertypes
+ 
+ clean:
+-	-rm -f ebtables
++	rm -f ebtables
+ 	rm -f *.o *.c~
+ 	rm -f extensions/*.o extensions/*.c~
+--- ebtables-v2.0pre6/ebtables.c	Fri May  3 21:33:55 2002
++++ ebtables-v2.0pre7.001/ebtables.c	Wed Jun  5 21:42:17 2002
+@@ -31,6 +31,7 @@
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <linux/br_db.h> // the database
+ #include <netinet/in.h>
++#include <netinet/ether.h>
+ #include <asm/types.h>
+ #include "include/ebtables_u.h"
+ 
+@@ -349,7 +350,7 @@
+ 	return 0;
+ }
+ 
+-// helper function: processes a line of data from the file /etc/etherproto
++// helper function: processes a line of data from the file /etc/ethertypes
+ int get_a_line(char *buffer, char *value, FILE *ifp)
+ {
+ 	int i, hlp;
+@@ -389,7 +390,7 @@
+ 			break;
+ 	}
+ 	if (i == 5) return -1;
+-	/* discard comments at the end of a line */
++	// discard comments at the end of a line
+ 	if (value[i] == '\t' || value[i] == ' ')
+ 		while (1) {
+ 			hlp = fscanf(ifp, "%c", &anotherhlp);
+@@ -493,14 +494,12 @@
+ 				printf("Broadcast");
+ 				goto endsrc;
+ 			}
+-			for (j = 0; j < ETH_ALEN; j++)
+-				printf("%02x%s", hlp->sourcemac[j],
+-				   (j == ETH_ALEN - 1) ? "" : ":");
++			printf("%s", ether_ntoa((struct ether_addr *)
++			   hlp->sourcemac));
+ 			if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
+ 				printf("/");
+-				for (j = 0; j < ETH_ALEN; j++)
+-					printf("%02x%s", hlp->sourcemsk[j],
+-					   (j == ETH_ALEN - 1) ? "" : ":");
++				printf("%s", ether_ntoa((struct ether_addr *)
++				   hlp->sourcemsk));
+ 			}
+ endsrc:
+ 			printf(", ");
+@@ -526,14 +525,12 @@
+ 				printf("Broadcast");
+ 				goto enddst;
+ 			}
+-			for (j = 0; j < ETH_ALEN; j++)
+-				printf("%02x%s", hlp->destmac[j],
+-				   (j == ETH_ALEN - 1) ? "" : ":");
++			printf("%s", ether_ntoa((struct ether_addr *)
++			   hlp->destmac));
+ 			if (memcmp(hlp->destmsk, hlpmsk, 6)) {
+ 				printf("/");
+-				for (j = 0; j < ETH_ALEN; j++)
+-					printf("%02x%s", hlp->destmsk[j],
+-					   (j == ETH_ALEN - 1) ? "" : ":");
++				printf("%s", ether_ntoa((struct ether_addr *)
++				   hlp->destmsk));
+ 			}
+ enddst:
+ 			printf(", ");
+@@ -1132,31 +1129,11 @@
+ }
+ 
+ // put the mac address into 6 (ETH_ALEN) bytes
+-int getmac(char *from, char *to)
+-{
+-	int i, tmp;
+-	char *buffer;
+-
+-	if (strlen(from) != 3 * ETH_ALEN - 1)
+-		return -1;
+-	for (i = 1; i < ETH_ALEN; i++) {
+-		if (from[i*3 - 1] != ':')
+-			return -1;
+-		from[i*3 - 1] = '\0';
+-	}
+-	for (i = 0; i < ETH_ALEN; i++) {
+-		tmp = strtol(from + i*3, &buffer, 16);
+-		if (*buffer != '\0' || tmp > 255 || tmp < 0)
+-			return -1;
+-		to[i] = (unsigned char) tmp;
+-	}
+-	return 0;
+-}
+-
+ int getmac_and_mask(char *from, char *to, char *mask)
+ {
+ 	char *p;
+ 	int i;
++	struct ether_addr *addr;
+ 
+ 	if (strcasecmp(from, "Unicast") == 0) {
+ 		memcpy(to, mac_type_unicast, ETH_ALEN);
+@@ -1175,12 +1152,14 @@
+ 	}
+ 	if ( (p = strrchr(from, '/')) != NULL) {
+ 		*p = '\0';
+-		if (getmac(p + 1, mask))
++		if (!(addr = ether_aton(p + 1)))
+ 			return -1;
++		memcpy(mask, addr, ETH_ALEN);
+ 	} else
+ 		memset(mask, 0xff, ETH_ALEN);
+-	if (getmac(from, to))
++	if (!(addr = ether_aton(from)))
+ 		return -1;
++	memcpy(to, addr, ETH_ALEN);
+ 	for (i = 0; i < ETH_ALEN; i++)
+ 		to[i] &= mask[i];
+ 	return 0;
+--- ebtables-v2.0pre6/communication.c	Fri May  3 21:08:24 2002
++++ ebtables-v2.0pre7.001/communication.c	Wed Jun  5 20:17:25 2002
+@@ -172,8 +172,9 @@
+ 	// give the data to the kernel
+ 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+-		print_error("Couldn't update kernel chains, you probably need "
+-		   "to insmod an extension");	
++		print_error("The kernel doesn't support a certain ebtables"
++		  " extension, consider recompiling your kernel or insmod"
++		  " the extension");	
+ }
+ 
+ // gets executed after deliver_table
+@@ -379,9 +380,9 @@
+ 	optlen = sizeof(struct ebt_replace);
+ 	strcpy(repl.name, u_repl->name);
+ 	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+-		print_error("A kernel module needed by your command is probably"
+-		            " not loaded. Try insmod ebtables or"
+-		            " insmod ebtable_%s", repl.name);
++		print_error("The %s table is not supported by the kernel,"
++		  " consider recompiling your kernel or try insmod ebt_%s",
++		  repl.name, repl.name);
+ 
+ 	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+ 		print_memory();
+--- ebtables-v2.0pre6/extensions/ebt_nat.c	Sat Apr 27 22:31:21 2002
++++ ebtables-v2.0pre7.001/extensions/ebt_nat.c	Wed Jun  5 21:43:11 2002
+@@ -3,6 +3,7 @@
+ #include <string.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
++#include <netinet/ether.h>
+ #include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+@@ -74,13 +75,15 @@
+ {
+ 	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
++	struct ether_addr *addr;
+ 
+ 	switch (c) {
+ 	case NAT_S:
+ 		check_option(flags, OPT_SNAT);
+ 		to_source_supplied = 1;
+-		if (getmac(optarg, natinfo->mac))
++		if (!(addr = ether_aton(optarg)))
+ 			print_error("Problem with specified to-source mac");
++		memcpy(natinfo->mac, addr, ETH_ALEN);
+ 		break;
+ 	case NAT_S_TARGET:
+ 		check_option(flags, OPT_SNAT_TARGET);
+@@ -106,14 +109,16 @@
+ {
+ 	int i;
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
++	struct ether_addr *addr;
+ 
+ 	switch (c) {
+ 	case NAT_D:
+ 		check_option(flags, OPT_DNAT);
+ 		to_dest_supplied = 1;
+-		if (getmac(optarg, natinfo->mac))
++		if (!(addr = ether_aton(optarg)))
+ 			print_error("Problem with specified "
+ 			            "to-destination mac");
++		memcpy(natinfo->mac, addr, ETH_ALEN);
+ 		break;
+ 	case NAT_D_TARGET:
+ 		check_option(flags, OPT_DNAT_TARGET);
+@@ -155,12 +160,9 @@
+    const struct ebt_entry_target *target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+-	int i;
+ 
+ 	printf("snat - to: ");
+-	for (i = 0; i < ETH_ALEN; i++)
+-		printf("%02x%s",
+-		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+ 	printf(" --snat-target %s", standard_targets[natinfo->target]);
+ }
+ 
+@@ -168,12 +170,9 @@
+    const struct ebt_entry_target *target)
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+-	int i;
+ 
+ 	printf("dnat - to: ");
+-	for (i = 0; i < ETH_ALEN; i++)
+-		printf("%02x%s",
+-		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
++	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+ 	printf(" --dnat-target %s", standard_targets[natinfo->target]);
+ }
+ 
+--- ebtables-v2.0pre6/ChangeLog	Mon May 20 13:49:27 2002
++++ ebtables-v2.0pre7.001/ChangeLog	Thu Jun  6 19:22:14 2002
+@@ -1,3 +1,6 @@
++20020606
++	* more useful message when the kernel can't find an ebtables module
++	* some minor code clean-up (no real impact).
+ 20020520
+ 	* update help for -s and -d
+ 	* add VLAN in ethertypes
+--- ebtables-v2.0pre6/include/ebtables_u.h	Fri May  3 21:08:25 2002
++++ ebtables-v2.0pre7.001/include/ebtables_u.h	Wed Jun  5 21:43:27 2002
+@@ -185,7 +185,7 @@
+ void get_dbinfo(struct brdb_dbinfo *nr);
+ void get_db(int len, struct brdb_dbentry *db);
+ void deliver_allowdb(__u16 *decision);
+-int getmac(char *from, char *to);
++int name_to_protocol(char *name);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
+ #define print_bug(format, args...) \
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff
new file mode 100644
index 0000000..851b2c6
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff
@@ -0,0 +1,2745 @@
+--- ebtables-v2.0pre7/Makefile	Thu Jun  6 19:18:29 2002
++++ ebtables-v2.0pre8.001/Makefile	Thu Jun 27 18:53:55 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre7 (June 2002)"
++PROGVERSION:="2.0pre8 (June 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre7/ebtables.c	Wed Jun  5 21:42:17 2002
++++ ebtables-v2.0pre8.001/ebtables.c	Thu Jun 27 18:53:55 2002
+@@ -34,17 +34,25 @@
+ #include <netinet/ether.h>
+ #include <asm/types.h>
+ #include "include/ebtables_u.h"
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/wait.h>
+ 
+ // here are the number-name correspondences kept for the ethernet
+ // frame type field
+ #define PROTOCOLFILE "/etc/ethertypes"
+ 
+-#define DATABASEHOOKNR NF_BR_NUMHOOKS
++#ifndef PROC_SYS_MODPROBE
++#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
++#endif
++
++#define DATABASEHOOKNR -2
+ #define DATABASEHOOKNAME "DB"
+ 
+ static char *prog_name = PROGNAME;
+ static char *prog_version = PROGVERSION;
+-char* hooknames[NF_BR_NUMHOOKS] = {
++char *hooknames[NF_BR_NUMHOOKS] =
++{
+ 	[NF_BR_PRE_ROUTING]"PREROUTING",
+ 	[NF_BR_LOCAL_IN]"INPUT",
+ 	[NF_BR_FORWARD]"FORWARD",
+@@ -79,6 +87,10 @@
+ 	{ "destination"   , required_argument, 0, 'd' },
+ 	{ "dst"           , required_argument, 0, 'd' },
+ 	{ "table"         , required_argument, 0, 't' },
++	{ "modprobe"      , required_argument, 0, 'M' },
++	{ "new-chain"     , required_argument, 0, 'N' },
++	{ "rename-chain"  , required_argument, 0, 'E' },
++	{ "delete-chain"  , required_argument, 0, 'X' },
+ 	{ 0 }
+ };
+ 
+@@ -89,10 +101,11 @@
+ 	"ACCEPT",
+ 	"DROP",
+ 	"CONTINUE",
++	"RETURN",
+ };
+ 
+-unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+-unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
++unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
++unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+ unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+ unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+ unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+@@ -326,10 +339,74 @@
+ 	tables = t;
+ }
+ 
+-// used to parse /etc/etherproto
++// blatently stolen (again) from iptables.c userspace program
++// find out where the modprobe utility is located
++static char *get_modprobe(void)
++{
++	int procfile;
++	char *ret;
++
++	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
++	if (procfile < 0)
++		return NULL;
++
++	ret = malloc(1024);
++	if (ret) {
++		switch (read(procfile, ret, 1024)) {
++		case -1: goto fail;
++		case 1024: goto fail; /* Partial read.  Wierd */
++		}
++		if (ret[strlen(ret)-1]=='\n')
++			ret[strlen(ret)-1]=0;
++		close(procfile);
++		return ret;
++	}
++ fail:
++	free(ret);
++	close(procfile);
++	return NULL;
++}
++
++// I hate stealing, really... Lets call it a tribute.
++int ebtables_insmod(const char *modname, const char *modprobe)
++{
++	char *buf = NULL;
++	char *argv[3];
++
++	// If they don't explicitly set it, read out of kernel
++	if (!modprobe) {
++		buf = get_modprobe();
++		if (!buf)
++			return -1;
++		modprobe = buf;
++	}
++
++	switch (fork()) {
++	case 0:
++		argv[0] = (char *)modprobe;
++		argv[1] = (char *)modname;
++		argv[2] = NULL;
++		execv(argv[0], argv);
++
++		/* not usually reached */
++		exit(0);
++	case -1:
++		return -1;
++
++	default: /* parent */
++		wait(NULL);
++	}
++
++	free(buf);
++	return 0;
++}
++
++
++// used to parse /etc/ethertypes
+ int disregard_whitespace(char *buffer, FILE *ifp)
+ {
+ 	int hlp;
++
+ 	buffer[0] = '\t';
+ 	while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+ 		hlp = fscanf(ifp, "%c", buffer);
+@@ -338,10 +415,11 @@
+ 	return 0;
+ }
+ 
+-// used to parse /etc/etherproto
++// used to parse /etc/ethertypes
+ int disregard_tabspace(char *buffer, FILE *ifp)
+ {
+ 	int hlp;
++
+ 	buffer[0] = '\t';
+ 	while (buffer[0] == '\t' || buffer[0] == ' ') {
+ 		hlp = fscanf(ifp, "%c", buffer);
+@@ -356,9 +434,10 @@
+ 	int i, hlp;
+ 	char anotherhlp;
+ 
+-	/* discard comment lines && whitespace*/
++	// discard comment lines and whitespace
+ 	while (1) {
+-		if (disregard_whitespace(buffer, ifp)) return -1;
++		if (disregard_whitespace(buffer, ifp))
++			return -1;
+ 		if (buffer[0] == '#')
+ 			while (1) {
+ 				hlp = fscanf(ifp, "%c", &anotherhlp);
+@@ -367,17 +446,20 @@
+ 				if (anotherhlp == '\n')
+ 					break;
+ 			}
+-		else break;
++		else
++			break;
+ 	}
+ 
+ 	// buffer[0] already contains the first letter
+ 	for (i = 1; i < 21; i++) {
+ 		hlp = fscanf(ifp, "%c", buffer + i);
+-		if (hlp == EOF || hlp == 0) return -1;
++		if (hlp == EOF || hlp == 0)
++			return -1;
+ 		if (buffer[i] == '\t' || buffer[i] == ' ')
+ 			break;
+ 	}
+-	if (i == 21) return -1;
++	if (i == 21)
++		return -1;
+ 	buffer[i] = '\0';
+ 	if (disregard_tabspace(value, ifp))
+ 		return -1;
+@@ -401,7 +483,8 @@
+ 	return 0;
+ }
+ 
+-// helper function for list_em()
++// translate a hexadecimal number to a protocol name, parsing /etc/ethertypes
++// returns 0 on success
+ int number_to_name(unsigned short proto, char *name)
+ {
+ 	FILE *ifp;
+@@ -425,7 +508,7 @@
+ }
+ 
+ // helper function for list_rules()
+-static void list_em(int hooknr)
++static void list_em(struct ebt_u_entries *entries)
+ {
+ 	int i, j, space = 0, digits;
+ 	struct ebt_u_entry *hlp;
+@@ -436,20 +519,21 @@
+ 	struct ebt_u_target *t;
+ 	char name[21];
+ 
+-	hlp = replace.hook_entry[hooknr]->entries;
+-	printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
+-	   standard_targets[replace.hook_entry[hooknr]->policy]);
+-	printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
++	hlp = entries->entries;
++	printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
++	   standard_targets[-entries->policy - 1]);
++	printf("nr. of entries: %d \n", entries->nentries);
+ 
+-	i = replace.hook_entry[hooknr]->nentries;
+-	while (i >9) {
++	i = entries->nentries;
++	while (i > 9) {
+ 		space++;
+ 		i /= 10;
+ 	}
+ 
+-	for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
++	for (i = 0; i < entries->nentries; i++) {
+ 		digits = 0;
+ 		// A little work to get nice rule numbers.
++		j = i + 1;
+ 		while (j > 9) {
+ 			digits++;
+ 			j /= 10;
+@@ -461,22 +545,22 @@
+ 		// Don't print anything about the protocol if no protocol was
+ 		// specified, obviously this means any protocol will do.
+ 		if (!(hlp->bitmask & EBT_NOPROTO)) {
+-			printf("eth proto: ");
++			printf("-p ");
+ 			if (hlp->invflags & EBT_IPROTO)
+ 				printf("! ");
+ 			if (hlp->bitmask & EBT_802_3)
+-				printf("Length, ");
++				printf("Length ");
+ 			else {
+ 				if (number_to_name(ntohs(hlp->ethproto), name))
+-					printf("0x%x, ", ntohs(hlp->ethproto));
++					printf("0x%x ", ntohs(hlp->ethproto));
+ 				else
+-					printf("%s, ", name);
++					printf("%s ", name);
+ 			}
+ 		}
+ 		if (hlp->bitmask & EBT_SOURCEMAC) {
+ 			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ 
+-			printf("source mac: ");
++			printf("-s ");
+ 			if (hlp->invflags & EBT_ISOURCE)
+ 				printf("! ");
+ 			if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
+@@ -502,12 +586,12 @@
+ 				   hlp->sourcemsk));
+ 			}
+ endsrc:
+-			printf(", ");
++			printf(" ");
+ 		}
+ 		if (hlp->bitmask & EBT_DESTMAC) {
+ 			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ 
+-			printf("dest mac: ");
++			printf("-d ");
+ 			if (hlp->invflags & EBT_IDEST)
+ 				printf("! ");
+ 			if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
+@@ -533,27 +617,31 @@
+ 				   hlp->destmsk));
+ 			}
+ enddst:
+-			printf(", ");
++			printf(" ");
+ 		}
+ 		if (hlp->in[0] != '\0') {
++			printf("-i ");
+ 			if (hlp->invflags & EBT_IIN)
+ 				printf("! ");
+-			printf("in-if: %s, ", hlp->in);
++			printf("%s ", hlp->in);
+ 		}
+ 		if (hlp->logical_in[0] != '\0') {
++			printf("--logical-in ");
+ 			if (hlp->invflags & EBT_ILOGICALIN)
+ 				printf("! ");
+-			printf("logical in-if: %s, ", hlp->logical_in);
++			printf("%s ", hlp->logical_in);
+ 		}
+ 		if (hlp->logical_out[0] != '\0') {
++			printf("--logical-out ");
+ 			if (hlp->invflags & EBT_ILOGICALOUT)
+ 				printf("! ");
+-			printf("logical out-if: %s, ", hlp->logical_out);
++			printf("%s, ", hlp->logical_out);
+ 		}
+ 		if (hlp->out[0] != '\0') {
++			printf("-o ");
+ 			if (hlp->invflags & EBT_IOUT)
+ 				printf("! ");
+-			printf("out-if: %s, ", hlp->out);
++			printf("%s, ", hlp->out);
+ 		}
+ 
+ 		m_l = hlp->m_list;
+@@ -573,30 +661,154 @@
+ 			w_l = w_l->next;
+ 		}
+ 
+-		printf("target: ");
++		printf("-j ");
++		if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
++			printf("%s ", hlp->t->u.name);
+ 		t = find_target(hlp->t->u.name);
+ 		if (!t)
+ 			print_bug("Target not found");
+ 		t->print(hlp, hlp->t);
+ 		printf(", count = %llu",
+-		   replace.counters[replace.counter_entry[hooknr] + i].pcnt);
++		   replace.counters[entries->counter_offset + i].pcnt);
+ 		printf("\n");
+ 		hlp = hlp->next;
+ 	}
+ }
+ 
++struct ebt_u_entries *nr_to_chain(int nr)
++{
++	if (nr == -1)
++		return NULL;
++	if (nr < NF_BR_NUMHOOKS)
++		return replace.hook_entry[nr];
++	else {
++		int i;
++		struct ebt_u_chain_list *cl = replace.udc;
++
++		i = nr - NF_BR_NUMHOOKS;
++		while (i > 0 && cl) {
++			cl = cl->next;
++			i--;
++		}
++		if (cl)
++			return cl->udc;
++		else
++			return NULL;
++	}
++}
++
++static struct ebt_u_entries *to_chain()
++{
++	return nr_to_chain(replace.selected_hook);
++}
++
++struct ebt_u_stack
++{
++	int chain_nr;
++	int n;
++	struct ebt_u_entry *e;
++	struct ebt_u_entries *entries;
++};
++
++void check_for_loops()
++{
++	int chain_nr , i, j , k, sp = 0, verdict;
++	struct ebt_u_entries *entries, *entries2;
++	struct ebt_u_stack *stack = NULL;
++	struct ebt_u_entry *e;
++
++	i = -1;
++	// initialize hook_mask to 0
++	while (1) {
++		i++;
++		if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
++			continue;
++		entries = nr_to_chain(i);
++		if (!entries)
++			break;
++		entries->hook_mask = 0;
++	}
++	if (i > NF_BR_NUMHOOKS) {
++		stack = (struct ebt_u_stack *)malloc((i - NF_BR_NUMHOOKS) *
++		   sizeof(struct ebt_u_stack));
++		if (!stack)
++			print_memory();
++	}
++
++	// check for loops, starting from every base chain
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (!(replace.valid_hooks & (1 << i)))
++			continue;
++		entries = nr_to_chain(i);
++		entries->hook_mask = (1 << i);
++		chain_nr = i;
++
++		e = entries->entries;
++		for (j = 0; j < entries->nentries; j++) {
++			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET))
++				goto letscontinue;
++			verdict = ((struct ebt_standard_target *)(e->t))->verdict;
++			if (verdict < 0)
++				goto letscontinue;
++			entries2 = nr_to_chain(verdict + NF_BR_NUMHOOKS);
++			entries2->hook_mask |= entries->hook_mask;
++			// now see if we've been here before
++			for (k = 0; k < sp; k++)
++				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS)
++					print_error("Loop from chain %s to chain %s",
++					   nr_to_chain(chain_nr)->name, nr_to_chain(stack[k].chain_nr)->name);
++			// jump to the chain, make sure we know how to get back
++			stack[sp].chain_nr = chain_nr;
++			stack[sp].n = j;
++			stack[sp].entries = entries;
++			stack[sp].e = e;
++			sp++;
++			j = -1;
++			e = entries2->entries;
++			chain_nr = verdict + NF_BR_NUMHOOKS;
++			entries = entries2;
++			continue;
++letscontinue:
++			e = e->next;
++		}
++		// we are at the end of a standard chain
++		if (sp == 0)
++			continue;
++		// go back to the chain one level higher
++		sp--;
++		j = stack[sp].n;
++		chain_nr = stack[sp].chain_nr;
++		e = stack[sp].e;
++		entries = stack[sp].entries;
++		goto letscontinue;
++	}
++	free(stack);
++	return;
++}
++
+ // parse the chain name and return the corresponding nr
++// returns -1 on failure
+ int get_hooknr(char* arg)
+ {
+ 	int i;
++	struct ebt_u_chain_list *cl = replace.udc;
+ 
+ 	// database is special case (not really a chain)
+ 	if (!strcmp(arg, DATABASEHOOKNAME))
+ 		return DATABASEHOOKNR;
+ 
+-	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+-		if (!strcmp(arg, hooknames[i]))
++	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++		if (!(replace.valid_hooks & (1 << i)))
++			continue;
++		if (!strcmp(arg, replace.hook_entry[i]->name))
++			return i;
++	}
++	while(cl) {
++		if (!strcmp(arg, cl->udc->name))
+ 			return i;
++		i++;
++		cl = cl->next;
++	}
+ 	return -1;
+ }
+ 
+@@ -623,6 +835,9 @@
+ "--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
+ "--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
+ "--policy -P chain target      : Change policy on chain to target\n"
++"--new-chain -N chain          : Create a user defined chain\n"
++"--rename-chain -E old new     : Rename a chain\n"
++"--delete-chain -X chain       : Delete a user defined chain\n"
+ "Options:\n"
+ "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+ "--src    -s [!] address[/mask]: source mac address\n"
+@@ -631,6 +846,7 @@
+ "--out-if -o [!] name          : network output interface name\n"
+ "--logical-in  [!] name        : logical bridge input interface name\n"
+ "--logical-out [!] name        : logical bridge output interface name\n"
++"--modprobe -M                 : try to insert modules using this command\n"
+ "--version -V                  : print package version\n"
+ "\n" ,
+ 	prog_name,
+@@ -661,22 +877,37 @@
+ 	int i;
+ 
+ 	printf("Bridge table: %s\n", table->name);
+-	if (replace.selected_hook != -1) list_em(replace.selected_hook);
+-	else
+-		for (i = 0; i < NF_BR_NUMHOOKS; i++)
+-			if (replace.valid_hooks & (1 << i))
+-				list_em(i);
+-	return;
++	if (replace.selected_hook != -1) {
++		list_em(to_chain());
++	} else {
++		struct ebt_u_chain_list *cl = replace.udc;
++
++		i = 0;
++		while (1) {
++			if (i < NF_BR_NUMHOOKS) {
++				if (replace.valid_hooks & (1 << i))
++					list_em(replace.hook_entry[i]);
++				i++;
++				continue;
++			} else {
++				if (!cl)
++					break;
++				list_em(cl->udc);
++				cl = cl->next;
++			}
++		}
++	}
+ }
+ 
+ // execute command P
+ static void change_policy(int policy)
+ {
+ 	int i;
++	struct ebt_u_entries *entries = to_chain();
+ 
+ 	// don't do anything if the policy is the same
+-	if (replace.hook_entry[replace.selected_hook]->policy != policy) {
+-		replace.hook_entry[replace.selected_hook]->policy = policy;
++	if (entries->policy != policy) {
++		entries->policy = policy;
+ 		replace.num_counters = replace.nentries;
+ 		if (replace.nentries) {
+ 			// '+ 1' for the CNT_END
+@@ -696,76 +927,105 @@
+ }
+ 
+ // flush one chain or the complete table
+-static void flush_chains()
++// -1 == nothing to do
++// 0 == give back to kernel
++static int flush_chains()
+ {
+-	int i, j, oldnentries;
++	int i, j, oldnentries, numdel;
+ 	unsigned short *cnt;
+ 	struct ebt_u_entry *u_e, *tmp;
++	struct ebt_u_entries *entries = to_chain();
+ 
+ 	// flush whole table
+-	if (replace.selected_hook == -1) {
++	if (!entries) {
+ 		if (replace.nentries == 0)
+-			exit(0);
++			return -1;
+ 		replace.nentries = 0;
+ 		// no need for the kernel to give us counters back
+ 		replace.num_counters = 0;
++
+ 		// free everything and zero (n)entries
+-		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+-			if (!(replace.valid_hooks & (1 << i)))
+-				continue;
+-			replace.hook_entry[i]->nentries = 0;
+-			u_e = replace.hook_entry[i]->entries;
++		i = -1;
++		while (1) {
++			i++;
++			entries = nr_to_chain(i);
++			if (!entries) {
++				if (i < NF_BR_NUMHOOKS)
++					continue;
++				else
++					break;
++			}
++			entries->nentries = 0;
++			entries->counter_offset = 0;
++			u_e = entries->entries;
++			entries->entries = NULL;
+ 			while (u_e) {
+ 				free_u_entry(u_e);
+ 				tmp = u_e->next;
+ 				free(u_e);
+ 				u_e = tmp;
+ 			}
+-			replace.hook_entry[i]->entries = NULL;
+ 		}
+-		return;
++		return 0;
+ 	}
+ 
+-	if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+-		exit(0);
++	if (entries->nentries == 0)
++		return -1;
+ 	oldnentries = replace.nentries;
+-	replace.nentries = replace.nentries -
+-	   replace.hook_entry[replace.selected_hook]->nentries;
++	replace.nentries -= entries->nentries;
++	numdel = entries->nentries;
+ 
+-	// delete the counters belonging to the specified chain
+ 	if (replace.nentries) {
+ 		// +1 for CNT_END
+ 		if ( !(counterchanges = (unsigned short *)
+ 		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+-		cnt = counterchanges;
+-		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+-			if (!(replace.valid_hooks & (1 << i)))
++	}
++	// delete the counters belonging to the specified chain,
++	// update counter_offset
++	i = -1;
++	cnt = counterchanges;
++	while (1) {
++		i++;
++		entries = nr_to_chain(i);
++		if (!entries) {
++			if (i < NF_BR_NUMHOOKS)
+ 				continue;
+-			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+-				if (i != replace.selected_hook)
+-					*cnt = CNT_NORM;
+-				else
++			else
++				break;
++		}
++		if (i > replace.selected_hook)
++			entries->counter_offset -= numdel;
++		if (replace.nentries) {
++			for (j = 0; j < entries->nentries; j++) {
++				if (i == replace.selected_hook)
+ 					*cnt = CNT_DEL;
++				else
++					*cnt = CNT_NORM;
+ 				cnt++;
+ 			}
+ 		}
++	}
++
++	if (replace.nentries) {
+ 		*cnt = CNT_END;
+ 		replace.num_counters = oldnentries;
+ 	}
+ 	else
+ 		replace.num_counters = 0;
+ 
+-	replace.hook_entry[replace.selected_hook]->nentries = 0;
+-	u_e = replace.hook_entry[replace.selected_hook]->entries;
++	entries = to_chain();
++	entries->nentries = 0;
++	u_e = entries->entries;
+ 	while (u_e) {
+ 		free_u_entry(u_e);
+ 		tmp = u_e->next;
+ 		free(u_e);
+ 		u_e = tmp;
+ 	}
+-	replace.hook_entry[replace.selected_hook]->entries = NULL;
+-}	
++	entries->entries = NULL;
++	return 0;
++}
+ 
+ // -1 == no match
+ static int check_rule_exists(int rule_nr)
+@@ -776,32 +1036,33 @@
+ 	struct ebt_u_watcher_list *w_l, *w_l2;
+ 	struct ebt_u_watcher *w;
+ 	struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
++	struct ebt_u_entries *entries = to_chain();
+ 	int i, j, k;
+ 
+ 	// handle '-D chain rulenr' command
+ 	if (rule_nr != -1) {
+-		if (rule_nr >
+-		   replace.hook_entry[replace.selected_hook]->nentries)
+-			return 0;
++		if (rule_nr > entries->nentries)
++			return -1;
+ 		// user starts counting from 1
+ 		return rule_nr - 1;
+ 	}
+-	u_e = replace.hook_entry[replace.selected_hook]->entries;
++	u_e = entries->entries;
+ 	// check for an existing rule (if there are duplicate rules,
+ 	// take the first occurance)
+-	for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
+-	   i++, u_e = u_e->next) {
++	for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+ 		if (!u_e)
+ 			print_bug("Hmm, trouble");
+ 		if ( u_e->ethproto == new_entry->ethproto
+ 		   && !strcmp(u_e->in, new_entry->in)
+-		   && !strcmp(u_e->out, new_entry->out)
+-		   && u_e->bitmask == new_entry->bitmask) {
++		   && !strcmp(u_e->out, new_entry->out)) {
++		   	if (strcmp(u_e->logical_in, new_entry->logical_in) ||
++			   strcmp(u_e->logical_out, new_entry->logical_out))
++				continue;
+ 			if (new_entry->bitmask & EBT_SOURCEMAC &&
+-			   strcmp(u_e->sourcemac, new_entry->sourcemac))
++			   memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+ 				continue;
+ 			if (new_entry->bitmask & EBT_DESTMAC &&
+-			   strcmp(u_e->destmac, new_entry->destmac))
++			   memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+ 				continue;
+ 			if (new_entry->bitmask != u_e->bitmask ||
+ 			   new_entry->invflags != u_e->invflags)
+@@ -863,7 +1124,7 @@
+ 	return -1;
+ }
+ 
+-// execute command A
++// execute command A or I
+ static void add_rule(int rule_nr)
+ {
+ 	int i, j;
+@@ -871,18 +1132,18 @@
+ 	unsigned short *cnt;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
++	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+ 	if (rule_nr != -1) { // command -I
+-		if (--rule_nr >
+-		   replace.hook_entry[replace.selected_hook]->nentries)
+-			print_error("rule nr too high: %d > %d", rule_nr,
+-			   replace.hook_entry[replace.selected_hook]->nentries);
++		if (--rule_nr > entries->nentries)
++			print_error("rule nr too high: %d > %d", rule_nr + 1,
++			   entries->nentries + 1);
+ 	} else
+-		rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
++		rule_nr = entries->nentries;
+ 	// we're adding one rule
+ 	replace.num_counters = replace.nentries;
+ 	replace.nentries++;
+-	replace.hook_entry[replace.selected_hook]->nentries++;
++	entries->nentries++;
+ 
+ 	// handle counter stuff
+ 	// +1 for CNT_END
+@@ -891,9 +1152,10 @@
+ 		print_memory();
+ 	cnt = counterchanges;
+ 	for (i = 0; i < replace.selected_hook; i++) {
+-		if (!(replace.valid_hooks & (1 << i)))
++		if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
+ 			continue;
+-		for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
++		entries2 = nr_to_chain(i);
++		for (j = 0; j < entries2->nentries; j++) {
+ 			*cnt = CNT_NORM;
+ 			cnt++;
+ 		}
+@@ -912,7 +1174,7 @@
+ 
+ 	// go to the right position in the chain
+ 	u_e2 = NULL;
+-	u_e = replace.hook_entry[replace.selected_hook]->entries;
++	u_e = entries->entries;
+ 	for (i = 0; i < rule_nr; i++) {
+ 		u_e2 = u_e;
+ 		u_e = u_e->next;
+@@ -921,7 +1183,7 @@
+ 	if (u_e2)
+ 		u_e2->next = new_entry;
+ 	else
+-		replace.hook_entry[replace.selected_hook]->entries = new_entry;
++		entries->entries = new_entry;
+ 	new_entry->next = u_e;
+ 
+ 	// put the ebt_[match, watcher, target] pointers in place
+@@ -936,6 +1198,20 @@
+ 		w_l = w_l->next;
+ 	}
+ 	new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
++
++	// update the counter_offset of chains behind this one
++	i = replace.selected_hook;
++	while (1) {
++		i++;
++		entries = nr_to_chain(i);
++		if (!entries) {
++			if (i < NF_BR_NUMHOOKS)
++				continue;
++			else
++				break;
++		} else
++			entries->counter_offset++;
++	}
+ }
+ 
+ // execute command D
+@@ -944,9 +1220,10 @@
+ 	int i, j, lentmp = 0;
+ 	unsigned short *cnt;
+ 	struct ebt_u_entry *u_e, *u_e2;
++	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+ 	if ( (i = check_rule_exists(rule_nr)) == -1 )
+-		print_error("Sorry, rule does not exists");
++		print_error("Sorry, rule does not exist");
+ 
+ 	// we're deleting a rule
+ 	replace.num_counters = replace.nentries;
+@@ -954,9 +1231,11 @@
+ 
+ 	if (replace.nentries) {
+ 		for (j = 0; j < replace.selected_hook; j++) {
+-			if (!(replace.valid_hooks & (1 << j)))
++			if (j < NF_BR_NUMHOOKS &&
++			   !(replace.valid_hooks & (1 << j)))
+ 				continue;
+-			lentmp += replace.hook_entry[j]->nentries;
++			entries2 = nr_to_chain(j);
++			lentmp += entries2->nentries;
+ 		}
+ 		lentmp += i;
+ 		// +1 for CNT_END
+@@ -981,7 +1260,7 @@
+ 
+ 	// go to the right position in the chain
+ 	u_e2 = NULL;
+-	u_e = replace.hook_entry[replace.selected_hook]->entries;
++	u_e = entries->entries;
+ 	for (j = 0; j < i; j++) {
+ 		u_e2 = u_e;
+ 		u_e = u_e->next;
+@@ -991,12 +1270,25 @@
+ 	if (u_e2)
+ 		u_e2->next = u_e->next;
+ 	else
+-		replace.hook_entry[replace.selected_hook]->entries = u_e->next;
++		entries->entries = u_e->next;
+ 
+-	replace.hook_entry[replace.selected_hook]->nentries--;
++	entries->nentries--;
+ 	// free everything
+ 	free_u_entry(u_e);
+ 	free(u_e);
++	// update the counter_offset of chains behind this one
++	i = replace.selected_hook;
++	while (1) {
++		i++;
++		entries = nr_to_chain(i);
++		if (!entries) {
++			if (i < NF_BR_NUMHOOKS)
++				continue;
++			else
++				break;
++		} else
++			entries->counter_offset--;
++	}
+ }
+ 
+ // execute command Z
+@@ -1005,15 +1297,16 @@
+ 
+ 	if (zerochain == -1) {
+ 		// tell main() we don't update the counters
+-		// this results in tricking the kernel to zero his counters,
++		// this results in tricking the kernel to zero its counters,
+ 		// naively expecting userspace to update its counters. Muahahaha
+ 		counterchanges = NULL;
+ 		replace.num_counters = 0;
+ 	} else {
+ 		int i, j;
+ 		unsigned short *cnt;
++		struct ebt_u_entries *entries = nr_to_chain(zerochain), *e2;
+ 
+-		if (replace.hook_entry[zerochain]->nentries == 0)
++		if (entries->nentries == 0)
+ 			exit(0);
+ 		counterchanges = (unsigned short *)
+ 		   malloc((replace.nentries + 1) * sizeof(unsigned short));
+@@ -1021,14 +1314,16 @@
+ 			print_memory();
+ 		cnt = counterchanges;
+ 		for (i = 0; i < zerochain; i++) {
+-			if (!(replace.valid_hooks & (1 << i)))
++			if (i < NF_BR_NUMHOOKS &&
++			   !(replace.valid_hooks & (1 << i)))
+ 				continue;
+-			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
++			e2 = nr_to_chain(i);
++			for (j = 0; j < e2->nentries; j++) {
+ 				*cnt = CNT_NORM;
+ 				cnt++;
+ 			}
+ 		}
+-		for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
++		for (i = 0; i < entries->nentries; i++) {
+ 			*cnt = CNT_ZERO;
+ 			cnt++;
+ 		}
+@@ -1100,15 +1395,17 @@
+ 	exit(0);
+ }
+ 
+-// set ethproto
+-int name_to_protocol(char *name)
++//  0 == success
++//  1 == success, but for the special 'protocol' LENGTH
++// -1 == failure
++int name_to_number(char *name, __u16 *proto)
+ {
+ 	FILE *ifp;
+ 	char buffer[21], value[5], *bfr;
+ 	unsigned short i;
+ 
+ 	if (!strcasecmp("LENGTH", name)) {
+-		new_entry->ethproto = 0;
++		*proto = 0;
+ 		new_entry->bitmask |= EBT_802_3;
+ 		return 1;
+ 	}
+@@ -1121,7 +1418,7 @@
+ 		i = (unsigned short) strtol(value, &bfr, 16);
+ 		if (*bfr != '\0')
+ 			return -1;
+-		new_entry->ethproto = i;
++		*proto = i;
+ 		fclose(ifp);
+ 		return 0;
+ 	}
+@@ -1165,6 +1462,66 @@
+ 	return 0;
+ }
+ 
++// executes the final_check() function for all extensions used by the rule
++void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
++{
++	struct ebt_u_match_list *m_l;
++	struct ebt_u_watcher_list *w_l;
++	struct ebt_u_target *t;
++	struct ebt_u_match *m;
++	struct ebt_u_watcher *w;
++
++	m_l = e->m_list;
++	w_l = e->w_list;
++	while (m_l) {
++		m = find_match(m_l->m->u.name);
++		m->final_check(e, m_l->m, replace.name,
++		   entries->hook_mask, 1);
++		m_l = m_l->next;
++	}
++	while (w_l) {
++		w = find_watcher(w_l->w->u.name);
++		w->final_check(e, w_l->w, replace.name,
++		   entries->hook_mask, 1);
++		w_l = w_l->next;
++	}
++	t = find_target(e->t->u.name);
++	t->final_check(e, e->t, replace.name,
++	   entries->hook_mask, 1);
++}
++
++// used for the -X command
++void check_for_references(int chain_nr)
++{
++	int i = -1, j;
++	struct ebt_u_entries *entries;
++	struct ebt_u_entry *e;
++
++	while (1) {
++		i++;
++		entries = nr_to_chain(i);
++		if (!entries) {
++			if (i < NF_BR_NUMHOOKS)
++				continue;
++			else
++				break;
++		}
++		e = entries->entries;
++		j = 0;
++		while (e) {
++			j++;
++			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
++				e = e->next;
++				continue;
++			}
++			if (((struct ebt_standard_target *)e->t)->verdict == chain_nr)
++				print_error("Can't delete the chain, it's referenced "
++				   "in chain %s, rule %d", entries->name, j);
++			e = e->next;
++		}
++	}
++}
++
+ int check_inverse(const char option[])
+ {
+ 	if (strcmp(option, "!") == 0) {
+@@ -1199,13 +1556,15 @@
+ 	int c, i;
+ 	// this special one for the -Z option (we can have -Z <this> -L <that>)
+ 	int zerochain = -1;
+-	int policy = -1;
++	int policy = 0;
+ 	int rule_nr = -1;// used for -D chain number
+ 	struct ebt_u_target *t;
+ 	struct ebt_u_match *m;
+ 	struct ebt_u_watcher *w;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
++	struct ebt_u_entries *entries;
++	const char *modprobe = NULL;
+ 
+ 	// initialize the table name, OPT_ flags, selected hook and command
+ 	strcpy(replace.name, "filter");
+@@ -1219,21 +1578,113 @@
+ 	// put some sane values in our new entry
+ 	initialize_entry(new_entry);
+ 
++	// The scenario induced by this loop makes that:
++	// '-t'  and '-M' (if specified) have to come before '-A' and the like
++
+ 	// getopt saves the day
+ 	while ((c = getopt_long(argc, argv,
+-	   "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
++	   "-A:D:I:N:E:X:L::Z::F::P:Vhi:o:j:p:b:s:d:t:M:", ebt_options, NULL)) != -1) {
+ 		switch (c) {
+ 
+ 		case 'A': // add a rule
+ 		case 'D': // delete a rule
+ 		case 'P': // define policy
+ 		case 'I': // insert a rule
++		case 'N': // make a user defined chain
++		case 'E': // rename chain
++		case 'X': // delete chain
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
++			if ( !(table = find_table(replace.name)) )
++				print_error("Bad table name");
++			// get the kernel's information
++			if (get_table(&replace)) {
++				ebtables_insmod("ebtables", modprobe);
++				if (get_table(&replace))
++					print_error("can't initialize ebtables "
++					"table %s", replace.name);
++			}
++			if (optarg[0] == '-')
++				print_error("No chain name specified");
++			if (c == 'N') {
++				struct ebt_u_chain_list *cl, **cl2;
++
++				if (get_hooknr(optarg) != -1)
++					print_error("Chain %s already exists",
++					   optarg);
++				if (find_target(optarg))
++					print_error("Target with name %s exists"
++					   , optarg);
++				if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
++					print_error("Chain name length can't exceed %d",
++					   EBT_CHAIN_MAXNAMELEN - 1);
++				cl = (struct ebt_u_chain_list *)
++				   malloc(sizeof(struct ebt_u_chain_list));
++				if (!cl)
++					print_memory();
++				cl->next = NULL;
++				cl->udc = (struct ebt_u_entries *)
++				   malloc(sizeof(struct ebt_u_entries));
++				if (!cl->udc)
++					print_memory();
++				cl->udc->nentries = 0;
++				cl->udc->policy = EBT_ACCEPT;
++				cl->udc->counter_offset = replace.nentries;
++				cl->udc->hook_mask = 0;
++				strcpy(cl->udc->name, optarg);
++				cl->udc->entries = NULL;
++				cl->kernel_start = NULL;
++				// put the new chain at the end
++				cl2 = &replace.udc;
++				while (*cl2)
++					cl2 = &((*cl2)->next);
++				*cl2 = cl;
++				break;
++			}
+ 			if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+-				print_error("Bad chain");
++				print_error("Chain %s doesn't exist", optarg);
++			if (c == 'E') {
++				if (optind >= argc || argv[optind][0] == '-')
++					print_error("No new chain name specified");
++				if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
++					print_error("Chain name len can't exceed %d",
++					   EBT_CHAIN_MAXNAMELEN - 1);
++				if (get_hooknr(argv[optind]) != -1)
++					print_error("Chain %s already exists",
++					   argv[optind]);
++				entries = to_chain();
++				strcpy(entries->name, argv[optind]);
++				optind++;
++				break;
++			}
++			if (c == 'X') {
++				struct ebt_u_chain_list *cl, **cl2;
++
++				if (replace.selected_hook < NF_BR_NUMHOOKS)
++					print_error("You can't remove a standard chain");
++				// if the chain is referenced, don't delete it
++				check_for_references(replace.selected_hook - NF_BR_NUMHOOKS);
++				flush_chains();
++				entries = to_chain();
++				if (replace.udc->udc == entries) {
++					cl = replace.udc;
++					replace.udc = replace.udc->next;
++					free(cl->udc);
++					free(cl);
++					break;
++				}
++				cl2 = &(replace.udc);
++				while ((*cl2)->next->udc != entries)
++					cl2 = &((*cl2)->next);
++				cl = (*cl2)->next;
++				(*cl2)->next = (*cl2)->next->next;
++				free(cl->udc);
++				free(cl);
++				break;
++			}
++
+ 			if (c == 'D' && optind < argc &&
+ 			   argv[optind][0] != '-') {
+ 				rule_nr = strtol(argv[optind], &buffer, 10);
+@@ -1245,13 +1696,16 @@
+ 			if (c == 'P') {
+ 				if (optind >= argc)
+ 					print_error("No policy specified");
+-				for (i = 0; i < 2; i++)
++				policy = 0;
++				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ 					if (!strcmp(argv[optind],
+ 					   standard_targets[i])) {
+-						policy = i;
++						policy = -i -1;
++						if (policy == EBT_CONTINUE)
++							policy = 0;
+ 						break;
+ 					}
+-				if (policy == -1)
++				if (policy == 0)
+ 					print_error("Wrong policy");
+ 				optind++;
+ 			}
+@@ -1286,6 +1740,15 @@
+ 					            " not allowed");
+ 				replace.flags |= OPT_COMMAND;
+ 			}
++			if ( !(table = find_table(replace.name)) )
++				print_error("Bad table name");
++			// get the kernel's information
++			if (get_table(&replace)) {
++				ebtables_insmod("ebtables", modprobe);
++				if (get_table(&replace))
++					print_error("can't initialize ebtables "
++					"table %s", replace.name);
++			}
+ 			i = -1;
+ 			if (optarg) {
+ 				if ( (i = get_hooknr(optarg)) == -1 )
+@@ -1312,6 +1775,12 @@
+ 			printf("%s, %s\n", prog_name, prog_version);
+ 			exit(0);
+ 
++		case 'M': // modprobe
++			if (replace.command != 'h')
++				print_error("Please put the -M option earlier");
++			modprobe = optarg;
++			break;
++
+ 		case 'h': // help
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+@@ -1342,8 +1811,10 @@
+ 			break;
+ 
+ 		case 't': // table
++			if (replace.command != 'h')
++				print_error("Please put the -t option first");
+ 			check_option(&replace.flags, OPT_TABLE);
+-			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
++			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+ 				print_error("Table name too long");
+ 			strcpy(replace.name, optarg);
+ 			break;
+@@ -1375,7 +1846,7 @@
+ 					print_error("No in-interface "
+ 					            "specified");
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interfacelength");
++					print_error("Illegal interface length");
+ 				strcpy(new_entry->in, argv[optind - 1]);
+ 				break;
+ 			}
+@@ -1393,7 +1864,7 @@
+ 					print_error("No logical in-interface "
+ 					            "specified");
+ 				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+-					print_error("Illegal interfacelength");
++					print_error("Illegal interface length");
+ 				strcpy(new_entry->logical_in, argv[optind - 1]);
+ 				break;
+ 			}
+@@ -1437,7 +1908,6 @@
+ 				break;
+ 			}
+ 			if (c == 'j') {
+-
+ 				check_option(&replace.flags, OPT_JUMP);
+ 				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ 					if (!strcmp(optarg,
+@@ -1445,12 +1915,30 @@
+ 						t = find_target(
+ 						   EBT_STANDARD_TARGET);
+ 						((struct ebt_standard_target *)
+-						   t->t)->verdict = i;
++						   t->t)->verdict = -i - 1;
+ 						break;
+ 					}
+-				// must be an extension then
+-				if (i == NUM_STANDARD_TARGETS) {
++				if (-i - 1 == EBT_RETURN) {
++					if (replace.selected_hook < NF_BR_NUMHOOKS)
++						print_error("Return target"
++						" only for user defined chains");
++				}
++				if (i != NUM_STANDARD_TARGETS)
++					break;
++				if ((i = get_hooknr(optarg)) != -1) {
++						if (i < NF_BR_NUMHOOKS)
++							print_error("don't jump"
++							  " to a standard chain");
++						t = find_target(
++						   EBT_STANDARD_TARGET);
++						((struct ebt_standard_target *)
++						   t->t)->verdict = i - NF_BR_NUMHOOKS;
++						break;
++					}
++				else {
++					// must be an extension then
+ 					struct ebt_u_target *t;
++
+ 					t = find_target(optarg);
+ 					// -j standard not allowed either
+ 					if (!t || t ==
+@@ -1504,10 +1992,14 @@
+ 				print_error("Problem with the specified "
+ 				            "protocol");
+ 			new_entry->ethproto = i;
+-			if (*buffer != '\0')
+-				if (name_to_protocol(argv[optind - 1]) == -1)
++			if (*buffer != '\0') {
++				if ((i = name_to_number(argv[optind - 1],
++				   &new_entry->ethproto)) == -1)
+ 					print_error("Problem with the specified"
+ 					            " protocol");
++				if (i == 1)
++					new_entry->bitmask |= EBT_802_3;
++			}
+ 			if (new_entry->ethproto < 1536 &&
+ 			   !(new_entry->bitmask & EBT_802_3))
+ 				print_error("Sorry, protocols have values above"
+@@ -1527,7 +2019,7 @@
+ 			t = (struct ebt_u_target *)new_entry->t;
+ 			if ((t->parse(c - t->option_offset, argv, argc,
+ 			   new_entry, &t->flags, &t->t)))
+-				continue;
++				goto check_extension;
+ 
+ 			// is it a match_option?
+ 			for (m = matches; m; m = m->next)
+@@ -1538,7 +2030,7 @@
+ 			if (m != NULL) {
+ 				if (m->used == 0)
+ 					add_match(m);
+-				continue;
++				goto check_extension;
+ 			}
+ 
+ 			// is it a watcher option?
+@@ -1551,9 +2043,15 @@
+ 				print_error("Unknown argument");
+ 			if (w->used == 0)
+ 				add_watcher(w);
++check_extension:
++			if (replace.command != 'A' && replace.command != 'I' &&
++			   replace.command != 'D')
++				print_error("extensions only for -A, -I and -D");
+ 		}
+ 	}
+ 
++	if ( !table && !(table = find_table(replace.name)) )
++		print_error("Bad table name");
+ 	// database stuff before ebtables stuff
+ 	if (replace.command == 'b')
+ 		allowdb(allowbc);
+@@ -1570,41 +2068,38 @@
+ 			print_error("Not enough information");
+ 	}
+ 
+-	if ( !(table = find_table(replace.name)) )
+-		print_error("Bad table name");
+-
+ 	// do this after parsing everything, so we can print specific info
+ 	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ 		print_help();
+ 
+ 	// do the final checks
+-	m_l = new_entry->m_list;
+-	w_l = new_entry->w_list;
+-	t = (struct ebt_u_target *)new_entry->t;
+-	while (m_l) {
+-		m = (struct ebt_u_match *)(m_l->m);
+-		m->final_check(new_entry, m->m, replace.name,
+-		   replace.selected_hook);
+-		m_l = m_l->next;
+-	}
+-	while (w_l) {
+-		w = (struct ebt_u_watcher *)(w_l->w);
+-		w->final_check(new_entry, w->w, replace.name,
+-		   replace.selected_hook);
+-		w_l = w_l->next;
++	if (replace.command == 'A' || replace.command == 'I' ||
++	   replace.command == 'D') {
++		// this will put the hook_mask right for the chains
++		check_for_loops();
++		entries = to_chain();
++		m_l = new_entry->m_list;
++		w_l = new_entry->w_list;
++		t = (struct ebt_u_target *)new_entry->t;
++		while (m_l) {
++			m = (struct ebt_u_match *)(m_l->m);
++			m->final_check(new_entry, m->m, replace.name,
++			   entries->hook_mask, 0);
++			m_l = m_l->next;
++		}
++		while (w_l) {
++			w = (struct ebt_u_watcher *)(w_l->w);
++			w->final_check(new_entry, w->w, replace.name,
++			   entries->hook_mask, 0);
++			w_l = w_l->next;
++		}
++		t->final_check(new_entry, t->t, replace.name,
++		   entries->hook_mask, 0);
+ 	}
+-	t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+-	
+ 	// so, the extensions can work with the host endian
+ 	// the kernel does not have to do this ofcourse
+ 	new_entry->ethproto = htons(new_entry->ethproto);
+ 
+-	// get the kernel's information
+-	get_table(&replace);
+-	// check if selected_hook is a valid_hook
+-	if (replace.selected_hook >= 0 &&
+-	   !(replace.valid_hooks & (1 << replace.selected_hook)))
+-		print_error("Bad chain name");
+ 	if (replace.command == 'P')
+ 		change_policy(policy);
+ 	else if (replace.command == 'L') {
+@@ -1616,12 +2111,38 @@
+ 	}
+ 	if (replace.flags & OPT_ZERO)
+ 		zero_counters(zerochain);
+-	else if (replace.command == 'F')
+-		flush_chains();
+-	else if (replace.command == 'A' || replace.command == 'I')
++	else if (replace.command == 'F') {
++		if (flush_chains() == -1)
++			exit(0);
++	} else if (replace.command == 'A' || replace.command == 'I') {
+ 		add_rule(rule_nr);
+-	else if (replace.command == 'D')
++		check_for_loops();
++		// do the final_check(), for all entries
++		// needed when adding a rule that has a chain target
++		i = -1;
++		while (1) {
++			struct ebt_u_entry *e;
++
++			i++;
++			entries = nr_to_chain(i);
++			if (!entries) {
++				if (i < NF_BR_NUMHOOKS)
++					continue;
++				else
++					break;
++			}
++			e = entries->entries;
++			while (e) {
++				// userspace extensions use host endian
++				e->ethproto = ntohs(e->ethproto);
++				do_final_checks(e, entries);
++				e->ethproto = htons(e->ethproto);
++				e = e->next;
++			}
++		}
++	} else if (replace.command == 'D')
+ 		delete_rule(rule_nr);
++	// commands -N, -E, -X fall through
+ 
+ 	if (table->check)
+ 		table->check(&replace);
+--- ebtables-v2.0pre7/communication.c	Wed Jun  5 20:17:25 2002
++++ ebtables-v2.0pre8.001/communication.c	Thu Jun 27 18:53:55 2002
+@@ -42,9 +42,11 @@
+ 	struct ebt_u_entry *e;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
++	struct ebt_u_chain_list *cl;
++	struct ebt_u_entries *entries;
+ 	char *p, *base;
+ 	int i, j;
+-	unsigned int entries_size = 0;
++	unsigned int entries_size = 0, *chain_offsets;
+ 
+ 	new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+ 	if (!new)
+@@ -54,15 +56,34 @@
+ 	new->nentries = u_repl->nentries;
+ 	new->num_counters = u_repl->num_counters;
+ 	new->counters = u_repl->counters;
+-	memcpy(new->counter_entry, u_repl->counter_entry,
+-	   sizeof(new->counter_entry));
++	// determine nr of udc
++	i = 0;
++	cl = u_repl->udc;
++	while (cl) {
++		i++;
++		cl = cl->next;
++	}
++	i += NF_BR_NUMHOOKS;
++	chain_offsets = (unsigned int *)malloc(i * sizeof(unsigned int));
+ 	// determine size
+-	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+-		if (!(new->valid_hooks & (1 << i)))
+-			continue;
++	i = 0;
++	cl = u_repl->udc;
++	while (1) {
++		if (i < NF_BR_NUMHOOKS) {
++			if (!(new->valid_hooks & (1 << i))) {
++				i++;
++				continue;
++			}
++			entries = u_repl->hook_entry[i];
++		} else {
++			if (!cl)
++				break;
++			entries = cl->udc;
++		}
++		chain_offsets[i] = entries_size;
+ 		entries_size += sizeof(struct ebt_entries);
+ 		j = 0;
+-		e = u_repl->hook_entry[i]->entries;
++		e = entries->entries;
+ 		while (e) {
+ 			j++;
+ 			entries_size += sizeof(struct ebt_entry);
+@@ -83,9 +104,12 @@
+ 			e = e->next;
+ 		}
+ 		// a little sanity check
+-		if (j != u_repl->hook_entry[i]->nentries)
++		if (j != entries->nentries)
+ 			print_bug("Wrong nentries: %d != %d, hook = %s", j,
+-			   u_repl->hook_entry[i]->nentries, hooknames[i]);
++			   entries->nentries, entries->name);
++		if (i >= NF_BR_NUMHOOKS)
++			cl = cl->next;
++		i++;
+ 	}
+ 
+ 	new->entries_size = entries_size;
+@@ -95,18 +119,31 @@
+ 
+ 	// put everything in one block
+ 	p = new->entries;
+-	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++	i = 0;
++	cl = u_repl->udc;
++	while (1) {
+ 		struct ebt_entries *hlp;
+ 
+-		if (!(new->valid_hooks & (1 << i)))
+-			continue;
+ 		hlp = (struct ebt_entries *)p;
+-		new->hook_entry[i] = hlp;
+-		hlp->nentries = u_repl->hook_entry[i]->nentries;
+-		hlp->policy = u_repl->hook_entry[i]->policy;
++		if (i < NF_BR_NUMHOOKS) {
++			if (!(new->valid_hooks & (1 << i))) {
++				i++;
++				continue;
++			}
++			entries = u_repl->hook_entry[i];
++			new->hook_entry[i] = hlp;
++		} else {
++			if (!cl)
++				break;
++			entries = cl->udc;
++		}
++		hlp->nentries = entries->nentries;
++		hlp->policy = entries->policy;
++		strcpy(hlp->name, entries->name);
++		hlp->counter_offset = entries->counter_offset;
+ 		hlp->distinguisher = 0; // make the kernel see the light
+ 		p += sizeof(struct ebt_entries);
+-		e = u_repl->hook_entry[i]->entries;
++		e = entries->entries;
+ 		while (e) {
+ 			struct ebt_entry *tmp = (struct ebt_entry *)p;
+ 
+@@ -148,16 +185,27 @@
+ 			tmp->target_offset = p - base;
+ 			memcpy(p, e->t, e->t->target_size +
+ 			   sizeof(struct ebt_entry_target));
++			if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
++				struct ebt_standard_target *st =
++				   (struct ebt_standard_target *)p;
++				// translate the jump to a udc
++				if (st->verdict >= 0)
++					st->verdict = chain_offsets[st->verdict + NF_BR_NUMHOOKS];
++			}
+ 			p += e->t->target_size +
+ 			   sizeof(struct ebt_entry_target);
+ 			tmp->next_offset = p - base;
+ 			e = e->next;
+ 		}
++		if (i >= NF_BR_NUMHOOKS)
++			cl = cl->next;
++		i++;
+ 	}
+ 
+ 	// sanity check
+ 	if (p - new->entries != new->entries_size)
+ 		print_bug("Entries_size bug");
++	free(chain_offsets);
+ 	return new;
+ }
+ 
+@@ -287,7 +335,7 @@
+ static int
+ ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
+    int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
+-   unsigned int valid_hooks)
++   unsigned int valid_hooks, char *base)
+ {
+ 	// an entry
+ 	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+@@ -332,6 +380,26 @@
+ 			            "userspace tool", t->u.name);
+ 		memcpy(new->t, t, t->target_size +
+ 		   sizeof(struct ebt_entry_target));
++		// deal with jumps to udc
++		if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
++			char *tmp = base;
++			int verdict = ((struct ebt_standard_target *)t)->verdict;
++			int i;
++			struct ebt_u_chain_list *cl;
++
++			if (verdict >= 0) {
++				tmp += verdict;
++				cl = u_repl->udc;
++				i = 0;
++				while (cl && cl->kernel_start != tmp) {
++					i++;
++					cl = cl->next;
++				}
++				if (!cl)
++					print_bug("can't find udc for jump");
++				((struct ebt_standard_target *)new->t)->verdict = i;
++			}
++		}
+ 
+ 		// I love pointers
+ 		**u_e = new;
+@@ -342,33 +410,82 @@
+ 	} else { // a new chain
+ 		int i;
+ 		struct ebt_entries *entries = (struct ebt_entries *)e;
+-		struct ebt_u_entries *new;
++		struct ebt_u_chain_list *cl;
+ 
+-		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+-			if (valid_hooks & (1 << i))
+-				break;
+-		if (i >= NF_BR_NUMHOOKS)
+-			print_bug("Not enough valid hooks");
+-		*hook = i;
+ 		if (*n != *cnt)
+ 			print_bug("Nr of entries in the chain is wrong");
+ 		*n = entries->nentries;
+ 		*cnt = 0;
+-		new = (struct ebt_u_entries *)
+-		   malloc(sizeof(struct ebt_u_entries));
+-		if (!new)
+-			print_memory();
++		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
++			if (valid_hooks & (1 << i))
++				break;
++		*hook = i;
++		// makes use of fact that standard chains come before udc
++		if (i >= NF_BR_NUMHOOKS) { // udc
++			i -= NF_BR_NUMHOOKS;
++			cl = u_repl->udc;
++			while (i-- > 0)
++				cl = cl->next;
++			*u_e = &(cl->udc->entries);
++		} else {
++			*u_e = &(u_repl->hook_entry[*hook]->entries);
++		}
++		return 0;
++	}
++}
++
++// initialize all chain headers
++static int
++ebt_translate_chains(struct ebt_entry *e, unsigned int *hook,
++   struct ebt_u_replace *u_repl, unsigned int valid_hooks)
++{
++	int i;
++	struct ebt_entries *entries = (struct ebt_entries *)e;
++	struct ebt_u_entries *new;
++	struct ebt_u_chain_list **chain_list;
++
++	if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
++			if (valid_hooks & (1 << i))
++				break;
++		// makes use of fact that standard chains come before udc
++		if (i >= NF_BR_NUMHOOKS) { // udc
++			chain_list = &u_repl->udc;
++			// add in the back
++			while (*chain_list)
++				chain_list = &((*chain_list)->next);
++			*chain_list = (struct ebt_u_chain_list *)
++			   malloc(sizeof(struct ebt_u_chain_list));
++			if (!(*chain_list))
++				print_memory();
++			(*chain_list)->next = NULL;
++			(*chain_list)->udc = (struct ebt_u_entries *)
++			   malloc(sizeof(struct ebt_u_entries));
++			if (!((*chain_list)->udc))
++				print_memory();
++			new = (*chain_list)->udc;
++			// ebt_translate_entry depends on this for knowing
++			// to which chain is being jumped
++			(*chain_list)->kernel_start = (char *)e;
++		} else {
++			*hook = i;
++			new = (struct ebt_u_entries *)
++			   malloc(sizeof(struct ebt_u_entries));
++			if (!new)
++				print_memory();
++			u_repl->hook_entry[*hook] = new;
++		}
+ 		new->nentries = entries->nentries;
+ 		new->policy = entries->policy;
+ 		new->entries = NULL;
+-		u_repl->hook_entry[*hook] = new;
+-		*u_e = &new->entries;
+-		return 0;
++		new->counter_offset = entries->counter_offset;
++		strcpy(new->name, entries->name);
+ 	}
++	return 0;
+ }
+ 
+ // talk with kernel to receive the kernel's table
+-void get_table(struct ebt_u_replace *u_repl)
++int get_table(struct ebt_u_replace *u_repl)
+ {
+ 	int i, j, k, hook;
+ 	socklen_t optlen;
+@@ -380,9 +497,7 @@
+ 	optlen = sizeof(struct ebt_replace);
+ 	strcpy(repl.name, u_repl->name);
+ 	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+-		print_error("The %s table is not supported by the kernel,"
+-		  " consider recompiling your kernel or try insmod ebt_%s",
+-		  repl.name, repl.name);
++		return -1;
+ 
+ 	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+ 		print_memory();
+@@ -407,17 +522,20 @@
+ 	u_repl->nentries = repl.nentries;
+ 	u_repl->num_counters = repl.num_counters;
+ 	u_repl->counters = repl.counters;
+-	memcpy(u_repl->counter_entry, repl.counter_entry,
+-	   sizeof(repl.counter_entry));
++	u_repl->udc = NULL;
+ 	hook = -1;
++	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
++	   &hook, u_repl, u_repl->valid_hooks);
+ 	i = 0; // holds the expected nr. of entries for the chain
+ 	j = 0; // holds the up to now counted entries for the chain
+ 	k = 0; // holds the total nr. of entries,
+ 	       // should equal u_repl->nentries afterwards
++	hook = -1;
+ 	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
+-	   &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
++	   &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks, repl.entries);
+ 	if (k != u_repl->nentries)
+ 		print_bug("Wrong total nentries");
++	return 0;
+ }
+ 
+ void get_dbinfo(struct brdb_dbinfo *nr)
+@@ -425,7 +543,7 @@
+ 	socklen_t optlen = sizeof(struct brdb_dbinfo);
+ 
+ 	get_sockfd();
+-	
++
+ 	if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
+ 		print_error("Sorry, br_db code probably not in kernel, "
+ 		            "try insmod br_db");
+--- ebtables-v2.0pre7/extensions/ebt_redirect.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_redirect.c	Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <string.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_redirect.h>
+@@ -33,7 +32,6 @@
+ 	return;
+ }
+ 
+-
+ #define OPT_REDIRECT_TARGET  0x01
+ static int parse(int c, char **argv, int argc,
+    const struct ebt_u_entry *entry, unsigned int *flags,
+@@ -48,7 +46,7 @@
+ 		check_option(flags, OPT_REDIRECT_TARGET);
+ 		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ 			if (!strcmp(optarg, standard_targets[i])) {
+-				redirectinfo->target = i;
++				redirectinfo->target = -i - 1;
+ 				break;
+ 			}
+ 		if (i == NUM_STANDARD_TARGETS)
+@@ -61,10 +59,11 @@
+ }
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_target *target, const char *name, unsigned int hook)
++   const struct ebt_entry_target *target, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+-	if ( (hook != NF_BR_PRE_ROUTING || strcmp(name, "nat")) &&
+-	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++	if ( ((hook_mask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
++	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+ 		print_error("Wrong chain for redirect");
+ }
+ 
+@@ -74,8 +73,10 @@
+ 	struct ebt_redirect_info *redirectinfo =
+ 	   (struct ebt_redirect_info *)target->data;
+ 
+-	printf("redirect");
+-	printf(" --redirect-target %s", standard_targets[redirectinfo->target]);
++	if (redirectinfo->target == EBT_ACCEPT)
++		return;
++	printf(" --redirect-target %s",
++	   standard_targets[-redirectinfo->target - 1]);
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/extensions/ebt_nat.c	Wed Jun  5 21:43:11 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_nat.c	Thu Jun 27 18:53:55 2002
+@@ -4,7 +4,6 @@
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/ether.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_nat.h>
+@@ -89,7 +88,7 @@
+ 		check_option(flags, OPT_SNAT_TARGET);
+ 		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ 			if (!strcmp(optarg, standard_targets[i])) {
+-				natinfo->target = i;
++				natinfo->target = -i - 1;
+ 				break;
+ 			}
+ 		if (i == NUM_STANDARD_TARGETS)
+@@ -124,7 +123,7 @@
+ 		check_option(flags, OPT_DNAT_TARGET);
+ 		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ 			if (!strcmp(optarg, standard_targets[i])) {
+-				natinfo->target = i;
++				natinfo->target = -i - 1;
+ 				break;
+ 			}
+ 		if (i == NUM_STANDARD_TARGETS)
+@@ -137,22 +136,24 @@
+ }
+ 
+ static void final_check_s(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_target *target, const char *name, unsigned int hook)
++   const struct ebt_entry_target *target, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+-	if (hook != NF_BR_POST_ROUTING || strcmp(name, "nat"))
++	if (!(hook_mask & (1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+ 		print_error("Wrong chain for snat");
+-	if (to_source_supplied == 0)
++	if (time == 0 && to_source_supplied == 0)
+ 		print_error("No snat address supplied");
+ }
+ 
+ static void final_check_d(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_target *target, const char *name, unsigned int hook)
++   const struct ebt_entry_target *target, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+-	if ( ((hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
++	if (((hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) ||
+ 	   strcmp(name, "nat")) &&
+-	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++	   ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+ 		print_error("Wrong chain for dnat");
+-	if (to_dest_supplied == 0)
++	if (time == 0 && to_dest_supplied == 0)
+ 		print_error("No dnat address supplied");
+ }
+ 
+@@ -161,9 +162,9 @@
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 
+-	printf("snat - to: ");
++	printf("--to-src ");
+ 	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+-	printf(" --snat-target %s", standard_targets[natinfo->target]);
++	printf(" --snat-target %s", standard_targets[-natinfo->target - 1]);
+ }
+ 
+ static void print_d(const struct ebt_u_entry *entry,
+@@ -171,9 +172,9 @@
+ {
+ 	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+ 
+-	printf("dnat - to: ");
++	printf("--to-dst ");
+ 	printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+-	printf(" --dnat-target %s", standard_targets[natinfo->target]);
++	printf(" --dnat-target %s", standard_targets[-natinfo->target - 1]);
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/extensions/ebt_ip.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_ip.c	Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <string.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_ip.h>
+@@ -156,7 +155,7 @@
+    unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+-	char *end, *buffer;
++	char *end;
+ 	int i;
+ 
+ 	switch (c) {
+@@ -194,7 +193,7 @@
+ 		if (optind > argc)
+ 			print_error("Missing ip tos argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+-		if (i < 0 || i > 255 || *buffer != '\0')
++		if (i < 0 || i > 255 || *end != '\0')
+ 			print_error("Problem with specified ip tos");
+ 		ipinfo->tos = i;
+ 		ipinfo->bitmask |= EBT_IP_TOS;
+@@ -219,7 +218,8 @@
+ }
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_match *match, const char *name, unsigned int hook)
++   const struct ebt_entry_match *match, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+ 	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+ 	   entry->ethproto != ETH_P_IP)
+@@ -234,34 +234,34 @@
+ 	int j;
+ 
+ 	if (ipinfo->bitmask & EBT_IP_SOURCE) {
+-		printf("source ip: ");
++		printf("--ip-src ");
+ 		if (ipinfo->invflags & EBT_IP_SOURCE)
+ 			printf("! ");
+ 		for (j = 0; j < 4; j++)
+ 			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
+ 			   (j == 3) ? "" : ".");
+-		printf("%s, ", mask_to_dotted(ipinfo->smsk));
++		printf("%s ", mask_to_dotted(ipinfo->smsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_DEST) {
+-		printf("dest ip: ");
++		printf("--ip-dst ");
+ 		if (ipinfo->invflags & EBT_IP_DEST)
+ 			printf("! ");
+ 		for (j = 0; j < 4; j++)
+ 			printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
+ 			   (j == 3) ? "" : ".");
+-		printf("%s, ", mask_to_dotted(ipinfo->dmsk));
++		printf("%s ", mask_to_dotted(ipinfo->dmsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_TOS) {
+-		printf("ip TOS: ");
++		printf("--ip-tos ");
+ 		if (ipinfo->invflags & EBT_IP_TOS)
+ 			printf("! ");
+-		printf("0x%02X, ", ipinfo->tos);
++		printf("0x%02X ", ipinfo->tos);
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_PROTO) {
+-		printf("ip proto: ");
++		printf("--ip-proto ");
+ 		if (ipinfo->invflags & EBT_IP_DEST)
+ 			printf("! ");
+-		printf("%d, ", ipinfo->protocol);
++		printf("%d ", ipinfo->protocol);
+ 	}
+ }
+ 
+--- ebtables-v2.0pre7/extensions/ebt_arp.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_arp.c	Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <stdlib.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_arp.h>
+@@ -76,9 +75,8 @@
+ #define OPT_PTYPE  0x04
+ #define OPT_IP_S   0x08
+ #define OPT_IP_D   0x10
+-static int parse(int c, char **argv, int argc,
+-	        const struct ebt_u_entry *entry, unsigned int *flags,
+-	        struct ebt_entry_match **match)
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+ 	int i;
+@@ -178,7 +176,8 @@
+ }
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+-const struct ebt_entry_match *match, const char *name, unsigned int hook)
++   const struct ebt_entry_match *match, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+ 	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+ 	   (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
+@@ -195,40 +194,40 @@
+ 	int i;
+ 
+ 	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+-		printf("arp opcode: ");
++		printf("--arp-op ");
+ 		if (arpinfo->invflags & EBT_ARP_OPCODE)
+ 			printf("! ");
+ 		printf("%d ", ntohs(arpinfo->opcode));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_HTYPE) {
+-		printf("arp htype: ");
++		printf("--arp-htype ");
+ 		if (arpinfo->invflags & EBT_ARP_HTYPE)
+ 			printf("! ");
+ 		printf("%d ", ntohs(arpinfo->htype));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_PTYPE) {
+-		printf("arp ptype: ");
++		printf("--arp-ptype ");
+ 		if (arpinfo->invflags & EBT_ARP_PTYPE)
+ 			printf("! ");
+ 		printf("0x%x ", ntohs(arpinfo->ptype));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+-		printf("arp src IP ");
++		printf("--arp-ip-src ");
+ 		if (arpinfo->invflags & EBT_ARP_SRC_IP)
+ 			printf("! ");
+ 		for (i = 0; i < 4; i++)
+ 			printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
+ 			   (i == 3) ? "" : ".");
+-		printf("%s, ", mask_to_dotted(arpinfo->smsk));
++		printf("%s ", mask_to_dotted(arpinfo->smsk));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+-		printf("arp dst IP ");
++		printf("--arp-ip-dst ");
+ 		if (arpinfo->invflags & EBT_ARP_DST_IP)
+ 			printf("! ");
+ 		for (i = 0; i < 4; i++)
+ 			printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
+ 			   (i == 3) ? "" : ".");
+-		printf("%s, ", mask_to_dotted(arpinfo->dmsk));
++		printf("%s ", mask_to_dotted(arpinfo->dmsk));
+ 	}
+ }
+ 
+--- ebtables-v2.0pre7/extensions/ebt_vlan.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_vlan.c	Thu Jun 27 18:53:55 2002
+@@ -1,44 +1,68 @@
+ /*
+- * Summary: ebt_vlan userspace module
+- * 
+- * Description: 802.1Q Virtual LAN match support module for ebtables project.
+- * Enable to match 802.1Q VLAN tagged frames by VLAN numeric 
+- * identifier (12-bites field) and frame priority (3-bites field)
++ * Summary: ebt_vlan - IEEE 802.1Q extension module for userspace
++ *
++ * Description: 802.1Q Virtual LAN match support module for ebtables project. 
++ * Enables to match 802.1Q:
++ * 1) VLAN-tagged frames by VLAN numeric identifier (12 - bits field)
++ * 2) Priority-tagged frames by user_priority (3 bits field)
++ * 3) Encapsulated Frame by ethernet protocol type/length
+  * 
+  * Authors:
+  * Bart De Schuymer <bart.de.schuymer@pandora.be>
+- * Nick Fedchik <nick@fedchik.org.ua>
+- *  
+- *  May, 2002
++ * Nick Fedchik <nick@fedchik.org.ua> 
++ * June, 2002
++ *
++ * License: GNU GPL 
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
+  */
+ 
+ #include <stdio.h>
+-#include <string.h>
+ #include <stdlib.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
++#include <string.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_vlan.h>
+ 
+-#define VLAN_ID    '1'
+-#define VLAN_PRIO  '2'
+-
++#define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_
++#define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_
++#define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "!" : ""
++
++#define VLAN_ID    0
++#define VLAN_PRIO  1
++#define VLAN_ENCAP 2
+ static struct option opts[] = {
+-	{"vlan-id", required_argument, 0, VLAN_ID},
+-	{"vlan-prio", required_argument, 0, VLAN_PRIO},
+-	{0}
++	{"vlan-id", required_argument, NULL, VLAN_ID},
++	{"vlan-prio", required_argument, NULL, VLAN_PRIO},
++	{"vlan-encap", required_argument, NULL, VLAN_ENCAP},
++	{NULL}
+ };
+ 
++
+ /*
+- * Print out help for ebtables -h vlan 
++ * Print out local help by "ebtables -h vlan" 
+  */
+ static void print_help ()
+ {
+-	printf ("802.1Q VLAN options:\n"
+-		"--vlan-id [!] id        : VLAN ID 1-4095 (integer)\n"
+-		"--vlan-prio [!] prio    : VLAN Priority 0-7 (integer)\n");
++	printf ("802.1Q VLAN extension options:\n"
++		"--vlan-id [!]id        : VLAN-tagged frame identifier, 0,1-4094 (integer)\n"
++		"--vlan-prio [!]prio    : Priority-tagged frame user_priority, 0-7 (integer)\n"
++		"--vlan-encap [!]proto  : Encapsulated protocol (hexadecimal)\n");
+ }
+ 
+ /*
+@@ -49,40 +73,58 @@
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) match->data;
+ 	/*
+-	 * Just clean initial values 
++	 * Set initial values 
+ 	 */
+-	vlaninfo->id = 0;
++	vlaninfo->id = 1;	/* Default VID for VLAN-tagged 802.1Q frames */
+ 	vlaninfo->prio = 0;
++	vlaninfo->encap = 0;
+ 	vlaninfo->invflags = 0;
+ 	vlaninfo->bitmask = 0;
+ }
+ 
++/*
++ * option flags definition 
++ */
+ #define OPT_VLAN_ID     0x01
+ #define OPT_VLAN_PRIO   0x02
++#define OPT_VLAN_ENCAP  0x04
++
++/*
++ * Parse passed arguments values (ranges, flags, etc...)
++ * int c - parameter number from static struct option opts[]
++ * int argc - total amout of arguments (std argc value)
++ * 
++ */
+ static int
+-parse (int c, char **argv, int argc,
+-       const struct ebt_u_entry *entry, unsigned int *flags,
+-       struct ebt_entry_match **match)
++parse (int c,
++       char **argv,
++       int argc,
++       const struct ebt_u_entry *entry,
++       unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) (*match)->data;
+-	unsigned short i;
++	unsigned long i;
+ 	char *end;
+-
++	__u16 encap;
+ 	switch (c) {
+ 	case VLAN_ID:
++		/*
++		 * ebtables.c:check_option(unsigned int *flags, unsigned int mask)
++		 * checking for multiple usage of same option 
++		 */
+ 		check_option (flags, OPT_VLAN_ID);
+ 		/*
+-		 * Check If we got inversed arg for VID,
++		 * Check If we got inversed arg for vlan-id option,
+ 		 * otherwise unset inversion flag 
+ 		 */
+ 		if (check_inverse (optarg))
+ 			vlaninfo->invflags |= EBT_VLAN_ID;
+ 		/*
+-		 * Check arg value presense 
++		 * Check arg value presence
+ 		 */
+ 		if (optind > argc)
+-			print_error ("Missing VLAN ID argument\n");
++			print_error ("Missing VLAN ID argument value\n");
+ 		/*
+ 		 * Convert argv to long int,
+ 		 * set *end to end of argv string, 
+@@ -90,15 +132,19 @@
+ 		 */
+ 		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
+ 		/*
+-		 * Check arg val range 
++		 * Check arg val range
+ 		 */
+-		if (i < 1 || i >= 4096 || *end != '\0') {
+-			i = 0;
++		if (i > 4094 || *end != '\0')
+ 			print_error
+-			    ("Problem with specified VLAN ID range\n");
+-		}
++			    ("Specified VLAN ID is out of range (0-4094)\n");
++		/*
++		 * Set up parameter value
++		 */
+ 		vlaninfo->id = i;
+-		vlaninfo->bitmask|=EBT_VLAN_ID;
++		/*
++		 * Set up parameter presence flag 
++		 */
++		SET_BITMASK (EBT_VLAN_ID);
+ 		break;
+ 
+ 	case VLAN_PRIO:
+@@ -107,25 +153,58 @@
+ 			vlaninfo->invflags |= EBT_VLAN_PRIO;
+ 		if (optind > argc)
+ 			print_error
+-			    ("Missing VLAN Priority level argument\n");
++			    ("Missing user_priority argument value\n");
+ 		/*
+ 		 * Convert argv to long int,
+ 		 * set *end to end of argv string, 
+ 		 * base set 10 for decimal only 
+ 		 */
+-		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
++		(unsigned char) i = strtol (argv[optind - 1], &end, 10);
+ 		/*
+ 		 * Check arg val range 
+ 		 */
+-		if (i >= 8 || *end != '\0') {
+-			i = 0;
++		if (i >= 8 || *end != '\0')
+ 			print_error
+-			    ("Problem with specified VLAN Priority range\n");
+-		}
++			    ("Specified user_priority is out of range (0-7)\n");
++		/*
++		 * Set up parameter value 
++		 */
+ 		vlaninfo->prio = i;
+-		vlaninfo->bitmask|=EBT_VLAN_PRIO;
++		/*
++		 * Set up parameter presence flag 
++		 */
++		SET_BITMASK (EBT_VLAN_PRIO);
+ 		break;
+ 
++	case VLAN_ENCAP:
++		check_option (flags, OPT_VLAN_ENCAP);
++		if (check_inverse (optarg))
++			vlaninfo->invflags |= EBT_VLAN_ENCAP;
++		if (optind > argc)
++			print_error
++			    ("Missing encapsulated frame type argument value\n");
++		/*
++		 * Parameter can be decimal, hexadecimal, or string.
++		 * Check arg val range (still raw area)
++		 */
++		(unsigned short) encap = strtol (argv[optind - 1], &end, 16);
++		if (*end == '\0' && (encap < ETH_ZLEN || encap > 0xFFFF))
++			print_error
++			    ("Specified encapsulated frame type is out of range\n");
++		if (*end != '\0')
++			if (name_to_number (argv[optind - 1], &encap) == -1)
++				print_error
++				    ("Problem with the specified encapsulated"
++				     "protocol\n");
++		/*
++		 * Set up parameter value (network notation)
++		 */
++		vlaninfo->encap = htons (encap);
++		/*
++		 * Set up parameter presence flag 
++		 */
++		SET_BITMASK (EBT_VLAN_ENCAP);
++		break;
+ 	default:
+ 		return 0;
+ 	}
+@@ -133,19 +212,31 @@
+ }
+ 
+ /*
+- * Final check 
++ * Final check - logical conditions
+  */
+ static void
+ final_check (const struct ebt_u_entry *entry,
+ 	     const struct ebt_entry_match *match,
+-	     const char *name, unsigned int hook)
++	     const char *name, unsigned int hook, unsigned int time)
+ {
++
++	struct ebt_vlan_info *vlaninfo =
++	    (struct ebt_vlan_info *) match->data;
+ 	/*
+-	 * Is any proto supplied there? Or specified proto isn't 802.1Q?
++	 * Is any proto param specified there? Or specified proto isn't 802.1Q?
+ 	 */
+ 	if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
+ 		print_error
+-		    ("For matching 802.1Q VLAN the protocol must be specified as 802_1Q\n");
++		    ("For use 802.1Q extension the protocol must be specified as 802_1Q\n");
++	/*
++	 * Check if specified vlan-id=0 (priority-tagged frame condition) 
++	 * when vlan-prio was specified.
++	 */
++	if (GET_BITMASK (EBT_VLAN_PRIO)) {
++		if (vlaninfo->id && GET_BITMASK (EBT_VLAN_ID))
++			print_error
++			    ("For use user_priority the specified vlan-id must be 0\n");
++	}
+ }
+ 
+ /*
+@@ -158,21 +249,36 @@
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) match->data;
+ 
++	char ethertype_name[21];
+ 	/*
+ 	 * Print VLAN ID if they are specified 
+ 	 */
+-	if (vlaninfo->bitmask & EBT_VLAN_ID) {
+-		printf ("vlan id: %s%d, ",
+-			vlaninfo->invflags & EBT_VLAN_ID ? "!" : "",
+-			vlaninfo->id);
++	if (GET_BITMASK (EBT_VLAN_ID)) {
++		printf ("--%s %s%d ",
++			opts[VLAN_ID].name,
++			INV_FLAG (EBT_VLAN_ID), vlaninfo->id);
+ 	}
+ 	/*
+-	 * Print VLAN priority if they are specified 
++	 * Print user priority if they are specified 
+ 	 */
+-	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+-		printf ("vlan prio: %s%d, ",
+-			vlaninfo->invflags & EBT_VLAN_PRIO ? "!" : "",
+-			vlaninfo->prio);
++	if (GET_BITMASK (EBT_VLAN_PRIO)) {
++		printf ("--%s %s%d ",
++			opts[VLAN_PRIO].name,
++			INV_FLAG (EBT_VLAN_PRIO), vlaninfo->prio);
++	}
++	/*
++	 * Print encapsulated frame type if they are specified 
++	 */
++	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++		printf ("--%s %s",
++			opts[VLAN_ENCAP].name, INV_FLAG (EBT_VLAN_ENCAP));
++		bzero (ethertype_name, 21);
++		if (!number_to_name
++		    (ntohs (vlaninfo->encap), ethertype_name)) {
++			printf ("%s ", ethertype_name);
++		} else {
++			printf ("%2.4X ", ntohs (vlaninfo->encap));
++		}
+ 	}
+ }
+ 
+@@ -207,6 +313,13 @@
+ 	 */
+ 	if (vlaninfo1->bitmask & EBT_VLAN_PRIO) {
+ 		if (vlaninfo1->prio != vlaninfo2->prio)
++			return 0;
++	};
++	/*
++	 * Compare VLAN Encap if they are present 
++	 */
++	if (vlaninfo1->bitmask & EBT_VLAN_ENCAP) {
++		if (vlaninfo1->encap != vlaninfo2->encap)
+ 			return 0;
+ 	};
+ 	return 1;
+--- ebtables-v2.0pre7/extensions/ebt_log.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_log.c	Thu Jun 27 18:53:55 2002
+@@ -2,7 +2,6 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_log.h>
+@@ -143,7 +142,8 @@
+ }
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_watcher *watcher, const char *name, unsigned int hook)
++   const struct ebt_entry_watcher *watcher, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+ 	return;
+ }
+@@ -153,13 +153,13 @@
+ {
+ 	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+ 
+-	printf("log: log-level = %s - log-prefix = \"%s\"",
++	printf("--log-level %s --log-prefix \"%s\"",
+ 		eight_priority[loginfo->loglevel].c_name,
+ 		loginfo->prefix);
+ 	if (loginfo->bitmask & EBT_LOG_IP)
+-		printf(" - log-ip");
++		printf(" --log-ip");
+ 	if (loginfo->bitmask & EBT_LOG_ARP)
+-		printf(" - log-arp");
++		printf(" --log-arp");
+ 	printf(" ");
+ }
+ 
+--- ebtables-v2.0pre7/extensions/ebt_standard.c	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_standard.c	Thu Jun 27 18:53:55 2002
+@@ -1,6 +1,6 @@
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ 
+@@ -26,21 +26,34 @@
+ }
+ 
+ static void final_check(const struct ebt_u_entry *entry,
+-   const struct ebt_entry_target *target, const char *name, unsigned int hook)
++   const struct ebt_entry_target *target, const char *name,
++   unsigned int hook_mask, unsigned int time)
+ {
+ }
+ 
++struct ebt_u_entries *nr_to_chain(int nr);
+ static void print(const struct ebt_u_entry *entry,
+    const struct ebt_entry_target *target)
+ {
+-	__u8 verdict = ((struct ebt_standard_target *)target)->verdict;
++	int verdict = ((struct ebt_standard_target *)target)->verdict;
+ 
++	if (verdict >= 0) {
++		struct ebt_u_entries *entries;
++
++		entries = nr_to_chain(verdict + NF_BR_NUMHOOKS);
++		printf("%s", entries->name);
++		return;
++	}
+ 	if (verdict == EBT_CONTINUE)
+-		printf("Continue ");
+-	else if (verdict ==  EBT_ACCEPT)
+-		printf("Accept ");
++		printf("CONTINUE ");
++	else if (verdict == EBT_ACCEPT)
++		printf("ACCEPT ");
++	else if (verdict == EBT_DROP)
++		printf("DROP ");
++	else if (verdict == EBT_RETURN)
++		printf("RETURN ");
+ 	else
+-		printf("Drop ");
++		print_error("BUG: Bad standard target"); // this is a bug
+ }
+ 
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/ChangeLog	Thu Jun  6 19:22:14 2002
++++ ebtables-v2.0pre8.001/ChangeLog	Thu Jun 27 18:53:55 2002
+@@ -1,6 +1,14 @@
+-20020606
+-	* more useful message when the kernel can't find an ebtables module
+-	* some minor code clean-up (no real impact).
++20020625
++	* user defined chains support: added -N, -X, -E options.
++20020621
++	* some unlogged changes (due to lazyness)
++	* change the output for -L to make it look like it would look when
++	  the user inputs the command.
++	* try to autoload modules
++	* some minor bugfixes
++	* add user defined chains support (without new commands yet,
++	  deliberately)
++	* comparing rules didn't take the logical devices into account
+ 20020520
+ 	* update help for -s and -d
+ 	* add VLAN in ethertypes
+--- ebtables-v2.0pre7/ebtables.8	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/ebtables.8	Thu Jun 27 18:53:55 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8  "01 May 2002"
++.TH EBTABLES 8  "26 June 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -21,14 +21,18 @@
+ .\"     
+ .\"
+ .SH NAME
+-ebtables(v.2.0) \- ethernet bridge packet table administration
++ebtables (v.2.0) \- ethernet bridge packet table administration
+ .SH SYNOPSIS
+-.BR "ebtables -[ADI] " "chain rule-specification [options]"
++.BR "ebtables -[ADI] " "chain rule-specification " [ options ]
+ .br
+ .BR "ebtables -P " "chain target"
+ .br
+ .BR "ebtables -[FLZ] [" "chain" "]"
+ .br
++.BR "ebtables -[NX] " chain
++.br
++.BR "ebtables -E " "old-chain-name new-chain-name"
++.br
+ .B "ebtables -L DB"
+ .br
+ .BR "ebtables -[b] [" "y/n" "]"
+@@ -53,6 +57,7 @@
+ .IR ACCEPT ,
+ .IR DROP ,
+ .IR CONTINUE ,
++.IR RETURN ,
+ an extention.
+ .PP
+ .I ACCEPT
+@@ -61,7 +66,11 @@
+ means the frame has to be dropped.
+ .I CONTINUE
+ means the next rule has to be checked. This can be handy to know how many
+-frames pass a certain point in the chain or to log those frames. For the
++frames pass a certain point in the chain or to log those frames.
++.I RETURN
++means stop traversing this chain and resume at the next rule in the
++previous (calling) chain.
++For the
+ other targets see the
+ .B "TARGET EXTENSIONS"
+ section.
+@@ -70,7 +79,7 @@
+ .TP
+ .B "-t, --table"
+ This option specifies the frame matching table which the command should
+-operate on. The tables are: 
++operate on. If specified it should be the first option. The tables are: 
+ .BR filter ,
+ this is the default table and contains three chains: 
+ .B INPUT 
+@@ -154,7 +163,23 @@
+ .B ACCEPT
+ , either
+ .BR DROP .
+-.SS PARAMETERS
++.TP
++.B "-N, --new-chain"
++Create a new user-defined chain by the given name.
++.TP
++.B "-X, --delete-chain"
++Delete the specified user-defined chain. There must be no references to the
++chain,
++.B ebtables
++will complain if there are.
++.TP
++.B "-E, --rename-chain"
++Rename the specified chain to the new name. This has no effect on the
++structure of the table. It is also allowed to rename a base chain, f.e.
++if you like PREBRIDGING more than PREROUTING. Be sure to talk about the
++standard chain names when you would ask a question on a mailing list.
++.SS
++PARAMETERS
+ The following parameters make up a rule specification (as used in the add
+ and delete commands). A "!" argument before the specification inverts the
+ test for that specification. Apart from these standard parameters, there are others, see
+@@ -265,6 +290,10 @@
+ .BR CONTINUE ,
+ or a target extension, see
+ .BR "TARGET EXTENSIONS" .
++.TP
++.BR "-M, --modprobe " "\fIcommand\fP"
++When talking to the kernel, use this
++.IR command " to try to automatically load missing kernel modules."
+ .SH MATCH EXTENSIONS
+ .B ebtables
+ extensions are precompiled into the userspace tool. So there is no need
+@@ -316,16 +345,23 @@
+ .BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+ The ARP IP destination address specification.
+ .SS vlan
+-Specify 802.1Q VLAN specific fields. These will only work if the protocol equals
+-.BR 802_1Q .
+-For more details see
++Specify 802.1Q Tag Control Information fields. These will only work if the protocol equals
++.BR 802_1Q.
++Also see extension help by 
+ .BR "ebtables -h vlan" .
+ .TP
+ .BR "--vlan-id " "[!] \fIid\fP"
+-The VLAN identifier (decimal number from 0 to 4095).
++The VLAN identifier field, VID (decimal number from 0 to 4094).
+ .TP
+ .BR "--vlan-prio " "[!] \fIprio\fP"
+-The VLAN priority type, this can be a decimal number from 0 to 7. The default value is 0.
++The user_priority field, this can be a decimal number from 0 to 7. 
++Required VID to be 0 (null VID) or not specified vlan-id parameter (in this case VID deliberately be set to 0).
++.TP
++.BR "--vlan-encap " "[!] \fItype\fP"
++The encapsulated ethernet frame type/length, this can be a hexadecimal number from 0x0000 to 0xFFFF.
++Usually it's 0x0800 (IPv4). See also 
++.B /etc/ethertypes 
++file.
+ .SH WATCHER EXTENSION(S)
+ Watchers are things that only look at frames passing by. These watchers only see the
+ frame if the frame passes all the matches of the rule.
+@@ -380,7 +416,8 @@
+ knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP doesn't
+-make sense, but you could do that too.
++make sense, but you could do that too. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .TP
+ .B dnat
+ The
+@@ -405,7 +442,8 @@
+ The default target is ACCEPT. Making it CONTINUE could let you use 
+ multiple target extensions on the same frame. Making it DROP only makes
+ sense in the BROUTING chain but using the redirect target is more logical
+-there.
++there. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .TP
+ .B redirect
+ The
+@@ -423,7 +461,8 @@
+ knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use 
+ multiple target extensions on the same frame. Making it DROP in the
+-BROUTING chain will let the frames be routed.
++BROUTING chain will let the frames be routed. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .SH FILES
+ .I /etc/ethertypes
+ .SH BUGS
+--- ebtables-v2.0pre7/ethertypes	Mon Jun  3 19:54:55 2002
++++ ebtables-v2.0pre8.001/ethertypes	Thu Jun 27 18:53:55 2002
+@@ -7,7 +7,7 @@
+ # programs using this file should not be case sensitive
+ # that's all :-))
+ IPV4 	0800	put your comments behind, on the same line, after a tab
+-X25	0800    or whitespace
++X25	0805    or whitespace
+ ARP	0806
+ 802_1Q	8100	802.1Q Virtual LAN tagged frame
+ IPX	8137
+@@ -30,5 +30,4 @@
+ PPP_SES	8864	PPPoE session messages
+ ATMMPOA	884C	MultiProtocol over ATM
+ ATMFATE	8884	Frame-based ATM Transport over Ethernet
+-
+-
++LOOP	9000
+--- ebtables-v2.0pre7/include/ebtables_u.h	Wed Jun  5 21:43:27 2002
++++ ebtables-v2.0pre8.001/include/ebtables_u.h	Thu Jun 27 18:53:55 2002
+@@ -28,11 +28,23 @@
+ 
+ struct ebt_u_entries
+ {
+-	__u8 policy;
++	int policy;
+ 	__u32 nentries;
++	// counter offset for this chain
++	unsigned int counter_offset;
++	// used for udc
++	unsigned int hook_mask;
++	char name[EBT_CHAIN_MAXNAMELEN];
+ 	struct ebt_u_entry *entries;
+ };
+ 
++struct ebt_u_chain_list
++{
++	struct ebt_u_entries *udc;
++	struct ebt_u_chain_list *next;
++	// this is only used internally, in communications.c
++	char *kernel_start;
++};
+ 
+ struct ebt_u_replace
+ {
+@@ -41,8 +53,8 @@
+ 	// nr of rules in the table
+ 	unsigned int nentries;
+ 	struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
+-	// how many counters in front of it?
+-	unsigned int counter_entry[NF_BR_NUMHOOKS];
++	// user defined chains (udc) list
++	struct ebt_u_chain_list *udc;
+ 	// nr of counters userspace expects back
+ 	unsigned int num_counters;
+ 	// where the kernel will put the old counters
+@@ -107,7 +119,7 @@
+ 	        struct ebt_entry_match **match);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_match *match,
+-	   const char *name, unsigned int hook);
++	   const char *name, unsigned int hook_mask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_match *match);
+ 	int (*compare)(const struct ebt_entry_match *m1,
+@@ -134,7 +146,7 @@
+ 	   struct ebt_entry_watcher **watcher);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_watcher *watch, const char *name,
+-	   unsigned int hook);
++	   unsigned int hook_mask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_watcher *watcher);
+ 	int (*compare)(const struct ebt_entry_watcher *w1,
+@@ -158,7 +170,7 @@
+ 	   struct ebt_entry_target **target);
+ 	void (*final_check)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_target *target, const char *name,
+-	   unsigned int hook);
++	   unsigned int hook_mask, unsigned int time);
+ 	void (*print)(const struct ebt_u_entry *entry,
+ 	   const struct ebt_entry_target *target);
+ 	int (*compare)(const struct ebt_entry_target *t1,
+@@ -175,7 +187,7 @@
+ void register_match(struct ebt_u_match *);
+ void register_watcher(struct ebt_u_watcher *);
+ void register_target(struct ebt_u_target *t);
+-void get_table(struct ebt_u_replace *repl);
++int get_table(struct ebt_u_replace *repl);
+ struct ebt_u_target *find_target(const char *name);
+ struct ebt_u_match *find_match(const char *name);
+ struct ebt_u_watcher *find_watcher(const char *name);
+@@ -185,7 +197,8 @@
+ void get_dbinfo(struct brdb_dbinfo *nr);
+ void get_db(int len, struct brdb_dbentry *db);
+ void deliver_allowdb(__u16 *decision);
+-int name_to_protocol(char *name);
++int name_to_number(char *name, __u16 *proto);
++int number_to_name(unsigned short proto, char *name);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
+ #define print_bug(format, args...) \
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre9.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre9.001.diff
new file mode 100644
index 0000000..6a51a84
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre9.001.diff
@@ -0,0 +1,38 @@
+--- ebtables-v2.0pre8/Makefile	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre9.001/Makefile	Sun Jul  7 16:29:50 2002
+@@ -2,7 +2,7 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre8 (June 2002)"
++PROGVERSION:="2.0pre9 (July 2002)"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -26,6 +26,8 @@
+ 		/usr/include/linux/br_db.h
+ 	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \
+ 		/usr/include/linux/netfilter_bridge.h
++	cp -f $(KERNEL_DIR)/include/linux/if_ether.h \
++		/usr/include/linux/if_ether.h
+ 
+ .PHONY: symlink
+ symlink:
+--- ebtables-v2.0pre8/ebtables.c	Thu Jun 27 18:53:55 2002
++++ ebtables-v2.0pre9.001/ebtables.c	Sat Jun 29 11:41:57 2002
+@@ -635,13 +635,13 @@
+ 			printf("--logical-out ");
+ 			if (hlp->invflags & EBT_ILOGICALOUT)
+ 				printf("! ");
+-			printf("%s, ", hlp->logical_out);
++			printf("%s ", hlp->logical_out);
+ 		}
+ 		if (hlp->out[0] != '\0') {
+ 			printf("-o ");
+ 			if (hlp->invflags & EBT_IOUT)
+ 				printf("! ");
+-			printf("%s, ", hlp->out);
++			printf("%s ", hlp->out);
+ 		}
+ 
+ 		m_l = hlp->m_list;
diff --git a/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.1.001.diff b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.1.001.diff
new file mode 100644
index 0000000..85e3949
--- /dev/null
+++ b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.1.001.diff
@@ -0,0 +1,289 @@
+--- ebtables-v2.0/Makefile	Thu Sep 19 19:52:09 2002
++++ ebtables-v2.0.1/Makefile	Thu Oct 17 23:27:29 2002
+@@ -2,8 +2,8 @@
+ 
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0"
+-PROGDATE:="September 2002"
++PROGVERSION:="2.0.1"
++PROGDATE:="October 2002"
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0/ebtables.c	Sat Aug 24 23:01:21 2002
++++ ebtables-v2.0.1/ebtables.c	Thu Oct 17 22:51:12 2002
+@@ -635,8 +635,9 @@
+ 			print_bug("Target not found");
+ 		t->print(hlp, hlp->t);
+ 		if (replace.flags & LIST_C)
+-			printf(", count = %llu",
+-			   replace.counters[entries->counter_offset + i].pcnt);
++			printf(", pcnt = %llu -- bcnt = %llu",
++			   replace.counters[entries->counter_offset + i].pcnt,
++			   replace.counters[entries->counter_offset + i].bcnt);
+ 		printf("\n");
+ 		hlp = hlp->next;
+ 	}
+--- ebtables-v2.0/extensions/ebt_ip.c	Thu Aug 29 18:48:36 2002
++++ ebtables-v2.0.1/extensions/ebt_ip.c	Thu Oct 17 23:21:16 2002
+@@ -1,7 +1,36 @@
++/*
++ *  ebtables ebt_ip: IP extension module for userspace
++ * 
++ *  Authors:
++ *   Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ *  Changes:
++ *    added ip-sport and ip-dport; parsing of port arguments is
++ *    based on code from iptables-1.2.7a
++ *    Innominate Security Technologies AG <mhopf@innominate.com>
++ *    September, 2002
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ *
++ */
++
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <getopt.h>
++#include <netdb.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_ip.h>
+ 
+@@ -9,16 +38,22 @@
+ #define IP_DEST   '2'
+ #define IP_myTOS  '3' // include/bits/in.h seems to already define IP_TOS
+ #define IP_PROTO  '4'
++#define IP_SPORT  '5'
++#define IP_DPORT  '6'
+ 
+ static struct option opts[] =
+ {
+-	{ "ip-source"     , required_argument, 0, IP_SOURCE },
+-	{ "ip-src"        , required_argument, 0, IP_SOURCE },
+-	{ "ip-destination", required_argument, 0, IP_DEST   },
+-	{ "ip-dst"        , required_argument, 0, IP_DEST   },
+-	{ "ip-tos"        , required_argument, 0, IP_myTOS  },
+-	{ "ip-protocol"   , required_argument, 0, IP_PROTO  },
+-	{ "ip-proto"      , required_argument, 0, IP_PROTO  },
++	{ "ip-source"           , required_argument, 0, IP_SOURCE },
++	{ "ip-src"              , required_argument, 0, IP_SOURCE },
++	{ "ip-destination"      , required_argument, 0, IP_DEST   },
++	{ "ip-dst"              , required_argument, 0, IP_DEST   },
++	{ "ip-tos"              , required_argument, 0, IP_myTOS  },
++	{ "ip-protocol"         , required_argument, 0, IP_PROTO  },
++	{ "ip-proto"            , required_argument, 0, IP_PROTO  },
++	{ "ip-source-port"      , required_argument, 0, IP_SPORT  },
++	{ "ip-sport"            , required_argument, 0, IP_SPORT  },
++	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
++	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
+ 	{ 0 }
+ };
+ 
+@@ -127,6 +162,56 @@
+ 	return buf;
+ }
+ 
++// transform a protocol and service name into a port number
++static uint16_t parse_port(const char *protocol, const char *name)
++{
++	struct servent *service;
++	char *end;
++	int port;
++
++	port = strtol(name, &end, 10);
++	if (*end != '\0') {
++		if (protocol && 
++		    (service = getservbyname(name, protocol)) != NULL)
++			return ntohs(service->s_port);
++	}
++	else if (port >= 0 || port <= 0xFFFF) {
++		return port;
++	}
++	print_error("Problem with specified %s port '%s'", 
++		    protocol?protocol:"", name);
++	return 0; /* never reached */
++}
++
++static void
++parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
++{
++	char *buffer;
++	char *cp;
++	
++	buffer = strdup(portstring);
++	if ((cp = strchr(buffer, ':')) == NULL)
++		ports[0] = ports[1] = parse_port(protocol, buffer);
++	else {
++		*cp = '\0';
++		cp++;
++		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
++		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
++		
++		if (ports[0] > ports[1])
++			print_error("Invalid portrange (min > max)");
++	}
++	free(buffer);
++}
++
++static void print_port_range(uint16_t *ports)
++{
++	if (ports[0] == ports[1])
++		printf("%d ", ports[0]);
++	else
++		printf("%d:%d ", ports[0], ports[1]);
++}
++
+ static void print_help()
+ {
+ 	printf(
+@@ -134,7 +219,9 @@
+ "--ip-src    [!] address[/mask]: ip source specification\n"
+ "--ip-dst    [!] address[/mask]: ip destination specification\n"
+ "--ip-tos    [!] tos           : ip tos specification\n"
+-"--ip-proto  [!] protocol      : ip protocol specification\n");
++"--ip-proto  [!] protocol      : ip protocol specification\n"
++"--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
++"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n");
+ }
+ 
+ static void init(struct ebt_entry_match *match)
+@@ -149,6 +236,8 @@
+ #define OPT_DEST   0x02
+ #define OPT_TOS    0x04
+ #define OPT_PROTO  0x08
++#define OPT_SPORT  0x10
++#define OPT_DPORT  0x20
+ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+    unsigned int *flags, struct ebt_entry_match **match)
+ {
+@@ -183,6 +272,27 @@
+ 			   &ipinfo->dmsk);
+ 		break;
+ 
++	case IP_SPORT:
++	case IP_DPORT:
++		if (c == IP_SPORT) {
++			check_option(flags, OPT_SPORT);
++			ipinfo->bitmask |= EBT_IP_SPORT;
++			if (check_inverse(optarg))
++				ipinfo->invflags |= EBT_IP_SPORT;
++		} else {
++			check_option(flags, OPT_DPORT);
++			ipinfo->bitmask |= EBT_IP_DPORT;
++			if (check_inverse(optarg))
++				ipinfo->invflags |= EBT_IP_DPORT;
++		}
++		if (optind > argc)
++			print_error("Missing port argument");
++		if (c == IP_SPORT)
++			parse_port_range(NULL, argv[optind - 1], ipinfo->sport);
++		else
++			parse_port_range(NULL, argv[optind - 1], ipinfo->dport);
++		break;
++
+ 	case IP_myTOS:
+ 		check_option(flags, OPT_TOS);
+ 		if (check_inverse(optarg))
+@@ -219,9 +329,19 @@
+    const struct ebt_entry_match *match, const char *name,
+    unsigned int hookmask, unsigned int time)
+ {
++ 	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
++
+ 	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO)
+ 		print_error("For IP filtering the protocol must be "
+ 		            "specified as IPv4");
++
++ 	if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
++ 		(!ipinfo->bitmask & EBT_IP_PROTO || 
++ 		ipinfo->invflags & EBT_IP_PROTO ||
++ 		(ipinfo->protocol!=IPPROTO_TCP && 
++ 			ipinfo->protocol!=IPPROTO_UDP)))
++ 		print_error("For port filtering the IP protocol must be "
++ 		            "either 6 (tcp) or 17 (udp)");
+ }
+ 
+ static void print(const struct ebt_u_entry *entry,
+@@ -260,6 +380,20 @@
+ 			printf("! ");
+ 		printf("%d ", ipinfo->protocol);
+ 	}
++	if (ipinfo->bitmask & EBT_IP_SPORT) {
++		printf("--ip-sport ");
++		if (ipinfo->invflags & EBT_IP_SPORT) {
++			printf("! ");
++		}
++		print_port_range(ipinfo->sport);
++	}
++	if (ipinfo->bitmask & EBT_IP_DPORT) {
++		printf("--ip-dport ");
++		if (ipinfo->invflags & EBT_IP_DPORT) {
++			printf("! ");
++		}
++		print_port_range(ipinfo->dport);
++	}
+ }
+ 
+ static int compare(const struct ebt_entry_match *m1,
+@@ -290,6 +424,14 @@
+ 	}
+ 	if (ipinfo1->bitmask & EBT_IP_PROTO) {
+ 		if (ipinfo1->protocol != ipinfo2->protocol)
++			return 0;
++	}
++	if (ipinfo1->bitmask & EBT_IP_SPORT) {
++		if (ipinfo1->sport != ipinfo2->sport)
++			return 0;
++	}
++	if (ipinfo1->bitmask & EBT_IP_DPORT) {
++		if (ipinfo1->dport != ipinfo2->dport)
+ 			return 0;
+ 	}
+ 	return 1;
+--- ebtables-v2.0/ebtables.8	Sun Aug 11 13:58:05 2002
++++ ebtables-v2.0.1/ebtables.8	Thu Oct 17 23:20:57 2002
+@@ -153,7 +153,8 @@
+ .br
+ .B "--Lc"
+ .br
+-Puts the counter value at the end of every rule.
++Shows the counters at the end of every rule, there is a frame counter
++(pcnt) and a byte counter (bcnt).
+ .br
+ .B "--Lx"
+ .br
+@@ -371,6 +372,19 @@
+ The ip protocol.
+ The flag
+ .B --ip-proto
++is an alias for this option.
++.TP
++.BR "--ip-source-port " "[!] \fIport\fP[:\fIport\fP]"
++The source port or port range for the ip protocols 6 (TCP) and 17
++(UDP). If the first port is omitted, "0" is assumed; if the last
++is omitted, "65535" is assumed. The flag
++.B --ip-sport
++is an alias for this option.
++.TP
++.BR "--ip-destination-port " "[!] \fIport\fP[:\fIport\fP]"
++The destination port or port range for ip protocols 6 (TCP) and
++17 (UDP). The flag
++.B --ip-dport
+ is an alias for this option.
+ .SS arp
+ Specify arp specific fields. These will only work if the protocol equals
diff --git a/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.2.001.diff b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.2.001.diff
new file mode 100644
index 0000000..d7b588c
--- /dev/null
+++ b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.2.001.diff
@@ -0,0 +1,2445 @@
+--- ebtables-v2.0.1/Makefile	Thu Oct 17 23:27:29 2002
++++ ebtables-v2.0.2/Makefile	Sat Dec  7 13:09:40 2002
+@@ -1,64 +1,86 @@
+ # ebtables Makefile
+ 
+-KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0.1"
+-PROGDATE:="October 2002"
++PROGVERSION:=2.0.2
++PROGDATE:=December\ 2002
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+ CC:=gcc
++
+ include extensions/Makefile
+ 
+-# Some kernel testers prefer to use a symlink for /usr/include/linux
+-ifeq ($(SYMLINK), y)
+-KERNEL_INCLUDES=symlink
++OBJECTS:=getethertype.o ebtables.o communication.o $(EXT_OBJS)
++
++# Use the option NONSTANDARD=y when you don't want to use the kernel includes
++# that are included in this package. You should set KERNEL_INCLUDES to
++# the right directory (eg /usr/src/linux/include).
++# You should only need this when compiling the CVS or when adding new code.
++ifeq ($(NONSTANDARD), y)
++KERNEL_INCLUDES?=/usr/include/
+ else
+-KERNEL_INCLUDES=headers
++KERNEL_INCLUDES:=include/
++endif
++
++ifeq ($(ETHERTYPESPATH),)
++ETHERTYPESPATH:=/etc/
+ endif
++ETHERTYPESFILE:=$(ETHERTYPESPATH)ethertypes
+ 
+-all:	ebtables
++PROGSPECS:=-DPROGVERSION=\"$(PROGVERSION)\" \
++	-DPROGNAME=\"$(PROGNAME)\" \
++	-DPROGDATE=\"$(PROGDATE)\" \
++	-D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\"
+ 
+-.PHONY: headers
+-headers:
+-	mkdir -p /usr/include/linux/netfilter_bridge
+-	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge/* \
+-		/usr/include/linux/netfilter_bridge/
+-	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \
+-		/usr/include/linux/netfilter_bridge.h
+-	cp -f $(KERNEL_DIR)/include/linux/if_ether.h \
+-		/usr/include/linux/if_ether.h
+-
+-.PHONY: symlink
+-symlink:
+-	rm -f /usr/include/linux
+-	ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux
++
++all: ebtables
+ 
+ communication.o: communication.c include/ebtables_u.h
+-	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" -c -o $@ $<
++	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
++
++getethertype.o: getethertype.c include/ethernetdb.h
++	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -Iinclude/
+ 
+ ebtables.o: ebtables.c include/ebtables_u.h
+-	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \
+-	-DPROGNAME=\"$(PROGNAME)\" -DPROGDATE=\"$(PROGDATE)\" -c -o $@ $<
++	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+ 
+-ebtables: ebtables.o communication.o $(EXT_OBJS)
+-	$(CC) $(CFLAGS) -o $@ $^
++ebtables: $(OBJECTS)
++	$(CC) $(CFLAGS) -o $@ $^ -I$(KERNEL_INCLUDES)
+ 
+ $(MANDIR)/man8/ebtables.8: ebtables.8
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
+ 
+-/etc/ethertypes: ethertypes
++$(ETHERTYPESFILE): ethertypes
+ 	mkdir -p $(@D)
+ 	install -m 0644 -o root -g root $< $@
++
+ .PHONY: exec
+ exec: ebtables
+ 	install -m 0755 -o root -g root $< /sbin/ebtables
+ 
+-install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
+-	ebtables /etc/ethertypes exec
++.PHONY: install
++install: $(MANDIR)/man8/ebtables.8 ebtables $(ETHERTYPESFILE) exec
+ 
++.PHONY: clean
+ clean:
+ 	rm -f ebtables
+ 	rm -f *.o *.c~
+ 	rm -f extensions/*.o extensions/*.c~
++
++DIR:=$(PROGNAME)-v$(PROGVERSION)
++# This is used to make a new userspace release
++.PHONY: release
++release:
++	mkdir -p include/linux/netfilter_bridge
++	install -m 0644 -o root -g root \
++		$(KERNEL_INCLUDES)/linux/netfilter_bridge.h include/linux/
++# To keep possible compile error complaints about undefined ETH_P_8021Q
++# off my back
++	install -m 0644 -o root -g root \
++		$(KERNEL_INCLUDES)/linux/if_ether.h include/linux/
++	install -m 0644 -o root -g root \
++		$(KERNEL_INCLUDES)/linux/netfilter_bridge/*.h \
++		include/linux/netfilter_bridge/
++	make clean
++	cd ..;tar -c $(DIR) | gzip >$(DIR).tar.gz
+--- ebtables-v2.0.1/ebtables.c	Thu Oct 17 22:51:12 2002
++++ ebtables-v2.0.2/ebtables.c	Tue Dec  3 22:52:12 2002
+@@ -28,11 +28,14 @@
+ #include <stdarg.h>
+ #include <netinet/ether.h>
+ #include "include/ebtables_u.h"
++#include "include/ethernetdb.h"
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+ 
+-// Don't use this function, use print_bug()
++/*
++ * Don't use this function, use print_bug()
++ */
+ void __print_bug(char *file, int line, char *format, ...)
+ {
+ 	va_list l;
+@@ -45,13 +48,10 @@
+ 	exit (-1);
+ }
+ 
+-// here are the number-name correspondences kept for the Ethernet
+-// frame type field
+-#define PROTOCOLFILE "/etc/ethertypes"
+-
+ #ifndef PROC_SYS_MODPROBE
+ #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+ #endif
++#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
+ 
+ char *hooknames[NF_BR_NUMHOOKS] =
+ {
+@@ -63,9 +63,11 @@
+ 	[NF_BR_BROUTING]"BROUTING"
+ };
+ 
+-// default command line options
+-// do not mess around with the already assigned numbers unless
+-// you know what you are doing
++/*
++ * default command line options
++ * do not mess around with the already assigned numbers unless
++ * you know what you are doing
++ */
+ static struct option ebt_original_options[] =
+ {
+ 	{ "append"        , required_argument, 0, 'A' },
+@@ -99,17 +101,16 @@
+ 	{ "new-chain"     , required_argument, 0, 'N' },
+ 	{ "rename-chain"  , required_argument, 0, 'E' },
+ 	{ "delete-chain"  , required_argument, 0, 'X' },
+-	{ "atomic-init"   , required_argument, 0, 7   },
+-	{ "atomic-commit" , required_argument, 0, 8   },
+-	{ "atomic"        , required_argument, 0, 9   },
+-	{ "atomic-save"   , required_argument, 0, 10  },
++	{ "atomic-init"   , no_argument      , 0, 7   },
++	{ "atomic-commit" , no_argument      , 0, 8   },
++	{ "atomic-file"   , required_argument, 0, 9   },
++	{ "atomic-save"   , no_argument      , 0, 10  },
+ 	{ "init-table"    , no_argument      , 0, 11  },
+ 	{ 0 }
+ };
+ 
+ static struct option *ebt_options = ebt_original_options;
+ 
+-// yup, all the possible target names
+ char* standard_targets[NUM_STANDARD_TARGETS] =
+ {
+ 	"ACCEPT",
+@@ -125,12 +126,18 @@
+ unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+ unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+ 
+-// holds all the data
++/*
++ * holds all the data
++ */
+ static struct ebt_u_replace replace;
+ 
+-// the chosen table
++/*
++ * the chosen table
++ */
+ static struct ebt_u_table *table = NULL;
+-// the lists of supported tables, matches, watchers and targets
++/*
++ * the lists of supported tables, matches, watchers and targets
++ */
+ static struct ebt_u_table *tables = NULL;
+ static struct ebt_u_match *matches = NULL;
+ static struct ebt_u_watcher *watchers = NULL;
+@@ -172,13 +179,15 @@
+ 	return t;
+ }
+ 
+-// The pointers in here are special:
+-// The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
+-// instead of making yet a few other structs, we just do a cast.
+-// We need a struct ebt_u_target pointer because we know the address of the data
+-// they point to won't change. We want to allow that the struct ebt_u_target.t
+-// member can change.
+-// Same holds for the struct ebt_match and struct ebt_watcher pointers
++/*
++ * The pointers in here are special:
++ * The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
++ * instead of making yet a few other structs, we just do a cast.
++ * We need a struct ebt_u_target pointer because we know the address of the data
++ * they point to won't change. We want to allow that the struct ebt_u_target.t
++ * member can change.
++ * Same holds for the struct ebt_match and struct ebt_watcher pointers
++ */
+ struct ebt_u_entry *new_entry;
+ 
+ static void initialize_entry(struct ebt_u_entry *e)
+@@ -199,7 +208,9 @@
+ 		print_bug("Couldn't load standard target");
+ }
+ 
+-// this doesn't free e, becoz the calling function might need e->next
++/*
++ * this doesn't free e, becoz the calling function might need e->next
++ */
+ static void free_u_entry(struct ebt_u_entry *e)
+ {
+ 	struct ebt_u_match_list *m_l, *m_l2;
+@@ -222,7 +233,9 @@
+ 	free(e->t);
+ }
+ 
+-// the user will use the match, so put it in new_entry
++/*
++ * the user will use the match, so put it in new_entry
++ */
+ static void add_match(struct ebt_u_match *m)
+ {
+ 	struct ebt_u_match_list **m_list, *new;
+@@ -350,8 +363,10 @@
+ 	tables = t;
+ }
+ 
+-// blatently stolen (again) from iptables.c userspace program
+-// find out where the modprobe utility is located
++/*
++ * blatently stolen (again) from iptables.c userspace program
++ * find out where the modprobe utility is located
++ */
+ static char *get_modprobe(void)
+ {
+ 	int procfile;
+@@ -411,59 +426,16 @@
+ 	return 0;
+ }
+ 
+-// helper function: processes a line of data from the file /etc/ethertypes
+-static int get_a_line(char *buffer, char *value, FILE *ifp)
+-{
+-	char line[80], *p;
+-	const char delim[] = " \t\n";
+-
+-	while (fgets(line, sizeof(line), ifp)) {
+-		p = strtok(line, delim);
+-		if (!p || p[0] == '#')
+-			continue;
+-		if (strlen(p) > 20)
+-			continue;
+-		strcpy(buffer, p);
+-		p = strtok(NULL, delim);
+-		if (!p || strlen(p) > 10)
+-			continue;
+-		strcpy(value, p);
+-		return 0;
+-	}
+-	return -1;
+-}
+-
+-// translate a hexadecimal number to a protocol name, parsing /etc/ethertypes
+-// returns 0 on success
+-// this demands the name buffer to be of size at least 21
+-int number_to_name(unsigned short proto, char *name)
+-{
+-	FILE *ifp;
+-	char buffer[21], value[11], *bfr;
+-	unsigned short i;
+-
+-	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+-		return -1;
+-	while (1) {
+-		if (get_a_line(buffer, value, ifp)) {
+-			fclose(ifp);
+-			return -1;
+-		}
+-		i = (unsigned short) strtol(value, &bfr, 16);
+-		if (*bfr != '\0' || i != proto)
+-			continue;
+-		strcpy(name, buffer);
+-		fclose(ifp);
+-		return 0;
+-	}
+-}
+-
+-// we use replace.flags, so we can't use the following values:
+-// 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
++/*
++ * we use replace.flags, so we can't use the following values:
++ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
++ */
+ #define LIST_N 0x04
+ #define LIST_C 0x08
+ #define LIST_X 0x10
+-// helper function for list_rules()
++/*
++ * helper function for list_rules()
++ */
+ static void list_em(struct ebt_u_entries *entries)
+ {
+ 	int i, j, space = 0, digits;
+@@ -473,7 +445,6 @@
+ 	struct ebt_u_match *m;
+ 	struct ebt_u_watcher *w;
+ 	struct ebt_u_target *t;
+-	char name[21];
+ 
+ 	hlp = entries->entries;
+ 	if (replace.flags & LIST_X && entries->policy != EBT_ACCEPT) {
+@@ -508,8 +479,10 @@
+ 			printf("ebtables -t %s -A %s ",
+ 			   replace.name, entries->name);
+ 
+-		// Don't print anything about the protocol if no protocol was
+-		// specified, obviously this means any protocol will do.
++		/*
++		 * Don't print anything about the protocol if no protocol was
++		 * specified, obviously this means any protocol will do.
++		 */
+ 		if (!(hlp->bitmask & EBT_NOPROTO)) {
+ 			printf("-p ");
+ 			if (hlp->invflags & EBT_IPROTO)
+@@ -517,10 +490,13 @@
+ 			if (hlp->bitmask & EBT_802_3)
+ 				printf("Length ");
+ 			else {
+-				if (number_to_name(ntohs(hlp->ethproto), name))
++				struct ethertypeent *ent;
++
++				ent = getethertypebynumber(ntohs(hlp->ethproto));
++				if (!ent)
+ 					printf("0x%x ", ntohs(hlp->ethproto));
+ 				else
+-					printf("%s ", name);
++					printf("%s ", ent->e_name);
+ 			}
+ 		}
+ 		if (hlp->bitmask & EBT_SOURCEMAC) {
+@@ -665,7 +641,7 @@
+ 	}
+ }
+ 
+-static struct ebt_u_entries *to_chain()
++static inline struct ebt_u_entries *to_chain()
+ {
+ 	return nr_to_chain(replace.selected_hook);
+ }
+@@ -686,7 +662,9 @@
+ 	struct ebt_u_entry *e;
+ 
+ 	i = -1;
+-	// initialize hook_mask to 0
++	/*
++	 * initialize hook_mask to 0
++	 */
+ 	while (1) {
+ 		i++;
+ 		if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
+@@ -703,13 +681,17 @@
+ 			print_memory();
+ 	}
+ 
+-	// check for loops, starting from every base chain
++	/*
++	 * check for loops, starting from every base chain
++	 */
+ 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 		if (!(replace.valid_hooks & (1 << i)))
+ 			continue;
+ 		entries = nr_to_chain(i);
+-		// (1 << NF_BR_NUMHOOKS) implies it's a standard chain
+-		// (usefull in the final_check() funtions)
++		/*
++		 * (1 << NF_BR_NUMHOOKS) implies it's a standard chain
++		 * (usefull in the final_check() funtions)
++		 */
+ 		entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
+ 		chain_nr = i;
+ 
+@@ -722,13 +704,17 @@
+ 				goto letscontinue;
+ 			entries2 = nr_to_chain(verdict + NF_BR_NUMHOOKS);
+ 			entries2->hook_mask |= entries->hook_mask;
+-			// now see if we've been here before
++			/*
++			 * now see if we've been here before
++			 */
+ 			for (k = 0; k < sp; k++)
+ 				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS)
+ 					print_error("Loop from chain %s to chain %s",
+ 					   nr_to_chain(chain_nr)->name,
+ 					   nr_to_chain(stack[k].chain_nr)->name);
+-			// jump to the chain, make sure we know how to get back
++			/*
++			 * jump to the chain, make sure we know how to get back
++			 */
+ 			stack[sp].chain_nr = chain_nr;
+ 			stack[sp].n = j;
+ 			stack[sp].entries = entries;
+@@ -742,10 +728,14 @@
+ letscontinue:
+ 			e = e->next;
+ 		}
+-		// we are at the end of a standard chain
++		/*
++		 * we are at the end of a standard chain
++		 */
+ 		if (sp == 0)
+ 			continue;
+-		// go back to the chain one level higher
++		/*
++		 * go back to the chain one level higher
++		 */
+ 		sp--;
+ 		j = stack[sp].n;
+ 		chain_nr = stack[sp].chain_nr;
+@@ -757,8 +747,10 @@
+ 	return;
+ }
+ 
+-// parse the chain name and return the corresponding nr
+-// returns -1 on failure
++/*
++ * parse the chain name and return the corresponding nr
++ * returns -1 on failure
++ */
+ int get_hooknr(char* arg)
+ {
+ 	int i;
+@@ -779,7 +771,6 @@
+ 	return -1;
+ }
+ 
+-// yup, print out help
+ static void print_help()
+ {
+ 	struct ebt_u_match_list *m_l;
+@@ -790,7 +781,8 @@
+ "ebtables -[ADI] chain rule-specification [options]\n"
+ "ebtables -P chain target\n"
+ "ebtables -[LFZ] [chain]\n"
+-"ebtables -[b] [y,n]\n"
++"ebtables -[NX] [chain]\n"
++"ebtables -E old-chain-name new-chain-name\n\n"
+ "Commands:\n"
+ "--append -A chain             : append to chain\n"
+ "--delete -D chain             : delete matching rule from chain\n"
+@@ -804,10 +796,10 @@
+ "--new-chain -N chain          : create a user defined chain\n"
+ "--rename-chain -E old new     : rename a chain\n"
+ "--delete-chain -X chain       : delete a user defined chain\n"
+-"--atomic-commit file          : update the kernel w/ the table contained in file\n"
+-"--atomic-init file            : put the initial kernel table into file\n"
+-"--atomic-save file            : put the current kernel table into file\n"
+-"--atomic file                 : write changes to file instead of kernel\n"
++"--atomic-commit               : update the kernel w/t table contained in <FILE>\n"
++"--atomic-init                 : put the initial kernel table into <FILE>\n"
++"--atomic-save                 : put the current kernel table into <FILE>\n"
++"--atomic file                 : set <FILE> to file\n\n"
+ "Options:\n"
+ "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+ "--src    -s [!] address[/mask]: source mac address\n"
+@@ -817,8 +809,10 @@
+ "--logical-in  [!] name        : logical bridge input interface name\n"
+ "--logical-out [!] name        : logical bridge output interface name\n"
+ "--modprobe -M program         : try to insert modules using this program\n"
+-"--version -V                  : print package version\n"
+-"\n");
++"--version -V                  : print package version\n\n"
++"Environment variable:\n"
++ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its value"
++"\n\n");
+ 
+ 	m_l = new_entry->m_list;
+ 	while (m_l) {
+@@ -839,7 +833,9 @@
+ 	exit(0);
+ }
+ 
+-// execute command L
++/*
++ * execute command L
++ */
+ static void list_rules()
+ {
+ 	int i;
+@@ -851,7 +847,9 @@
+ 	} else {
+ 		struct ebt_u_chain_list *cl = replace.udc;
+ 
+-		// create new chains and rename standard chains when necessary
++		/*
++		 * create new chains and rename standard chains when necessary
++		 */
+ 		if (replace.flags & LIST_X) {
+ 			while (cl) {
+ 				printf("ebtables -t %s -N %s\n", replace.name,
+@@ -883,22 +881,30 @@
+ 	}
+ }
+ 
+-// execute command P
++/*
++ * execute command P
++ */
+ static void change_policy(int policy)
+ {
+ 	int i;
+ 	struct ebt_u_entries *entries = to_chain();
+ 
+-	// don't do anything if the policy is the same
++	/*
++	 * don't do anything if the policy is the same
++	 */
+ 	if (entries->policy != policy) {
+ 		entries->policy = policy;
+ 		replace.num_counters = replace.nentries;
+ 		if (replace.nentries) {
+-			// '+ 1' for the CNT_END
++			/*
++			 * '+ 1' for the CNT_END
++			 */
+ 			if (!(replace.counterchanges = (unsigned short *) malloc(
+ 			   (replace.nentries + 1) * sizeof(unsigned short))))
+ 				print_memory();
+-			// done nothing special to the rules
++			/*
++			 * done nothing special to the rules
++			 */
+ 			for (i = 0; i < replace.nentries; i++)
+ 				replace.counterchanges[i] = CNT_NORM;
+ 			replace.counterchanges[replace.nentries] = CNT_END;
+@@ -910,9 +916,11 @@
+ 		exit(0);
+ }
+ 
+-// flush one chain or the complete table
+-// -1 == nothing to do
+-// 0 == give back to kernel
++/*
++ * flush one chain or the complete table
++ * -1 == nothing to do
++ * 0 == give back to kernel
++ */
+ static int flush_chains()
+ {
+ 	int i, j, oldnentries, numdel;
+@@ -920,15 +928,21 @@
+ 	struct ebt_u_entry *u_e, *tmp;
+ 	struct ebt_u_entries *entries = to_chain();
+ 
+-	// flush whole table
++	/*
++	 * flush whole table
++	 */
+ 	if (!entries) {
+ 		if (replace.nentries == 0)
+ 			return -1;
+ 		replace.nentries = 0;
+-		// no need for the kernel to give us counters back
++		/*
++		 * no need for the kernel to give us counters back
++		 */
+ 		replace.num_counters = 0;
+ 
+-		// free everything and zero (n)entries
++		/*
++		 * free everything and zero (n)entries
++		 */
+ 		i = -1;
+ 		while (1) {
+ 			i++;
+@@ -960,13 +974,17 @@
+ 	numdel = entries->nentries;
+ 
+ 	if (replace.nentries) {
+-		// +1 for CNT_END
++		/*
++		 * +1 for CNT_END
++		 */
+ 		if ( !(replace.counterchanges = (unsigned short *)
+ 		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+ 	}
+-	// delete the counters belonging to the specified chain,
+-	// update counter_offset
++	/*
++	 * delete the counters belonging to the specified chain,
++	 * update counter_offset
++	 */
+ 	i = -1;
+ 	cnt = replace.counterchanges;
+ 	while (1) {
+@@ -994,8 +1012,7 @@
+ 	if (replace.nentries) {
+ 		*cnt = CNT_END;
+ 		replace.num_counters = oldnentries;
+-	}
+-	else
++	} else
+ 		replace.num_counters = 0;
+ 
+ 	entries = to_chain();
+@@ -1011,7 +1028,9 @@
+ 	return 0;
+ }
+ 
+-// -1 == no match
++/*
++ * -1 == no match
++ */
+ static int check_rule_exists(int rule_nr)
+ {
+ 	struct ebt_u_entry *u_e;
+@@ -1023,16 +1042,22 @@
+ 	struct ebt_u_entries *entries = to_chain();
+ 	int i, j, k;
+ 
+-	// handle '-D chain rulenr' command
++	/*
++	 * handle '-D chain rulenr' command
++	 */
+ 	if (rule_nr != -1) {
+ 		if (rule_nr > entries->nentries)
+ 			return -1;
+-		// user starts counting from 1
++		/*
++		 * user starts counting from 1
++		 */
+ 		return rule_nr - 1;
+ 	}
+ 	u_e = entries->entries;
+-	// check for an existing rule (if there are duplicate rules,
+-	// take the first occurance)
++	/*
++	 * check for an existing rule (if there are duplicate rules,
++	 * take the first occurance)
++	 */
+ 	for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+ 		if (!u_e)
+ 			print_bug("Hmm, trouble");
+@@ -1055,7 +1080,9 @@
+ 		if (new_entry->bitmask != u_e->bitmask ||
+ 		   new_entry->invflags != u_e->invflags)
+ 			continue;
+-		// compare all matches
++		/*
++		 * compare all matches
++		 */
+ 		m_l = new_entry->m_list;
+ 		j = 0;
+ 		while (m_l) {
+@@ -1068,7 +1095,9 @@
+ 			j++;
+ 			m_l = m_l->next;
+ 		}
+-		// now be sure they have the same nr of matches
++		/*
++		 * now be sure they have the same nr of matches
++		 */
+ 		k = 0;
+ 		m_l = u_e->m_list;
+ 		while (m_l) {
+@@ -1078,7 +1107,9 @@
+ 		if (j != k)
+ 			continue;
+ 
+-		// compare all watchers
++		/*
++		 * compare all watchers
++		 */
+ 		w_l = new_entry->w_list;
+ 		j = 0;
+ 		while (w_l) {
+@@ -1119,19 +1150,23 @@
+ 	struct ebt_u_watcher_list *w_l;
+ 	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+-	if (rule_nr != -1) { // command -I
++	if (rule_nr != -1) { /* command -I */
+ 		if (--rule_nr > entries->nentries)
+ 			print_error("rule nr too high: %d > %d", rule_nr + 1,
+ 			   entries->nentries + 1);
+ 	} else
+ 		rule_nr = entries->nentries;
+-	// we're adding one rule
++	/*
++	 * we're adding one rule
++	 */
+ 	replace.num_counters = replace.nentries;
+ 	replace.nentries++;
+ 	entries->nentries++;
+ 
+-	// handle counter stuff
+-	// +1 for CNT_END
++	/*
++	 * handle counter stuff
++	 * +1 for CNT_END
++	 */
+ 	if ( !(replace.counterchanges = (unsigned short *)
+ 	   malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+ 		print_memory();
+@@ -1157,15 +1192,21 @@
+ 	}
+ 	*cnt = CNT_END;
+ 
+-	// go to the right position in the chain
++	/*
++	 * go to the right position in the chain
++	 */
+ 	u_e = &entries->entries;
+ 	for (i = 0; i < rule_nr; i++)
+ 		u_e = &(*u_e)->next;
+-	// insert the rule
++	/*
++	 * insert the rule
++	 */
+ 	new_entry->next = *u_e;
+ 	*u_e = new_entry;
+ 
+-	// put the ebt_[match, watcher, target] pointers in place
++	/*
++	 * put the ebt_[match, watcher, target] pointers in place
++	 */
+ 	m_l = new_entry->m_list;
+ 	while (m_l) {
+ 		m_l->m = ((struct ebt_u_match *)m_l->m)->m;
+@@ -1178,7 +1219,9 @@
+ 	}
+ 	new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
+ 
+-	// update the counter_offset of chains behind this one
++	/*
++	 * update the counter_offset of chains behind this one
++	 */
+ 	i = replace.selected_hook;
+ 	while (1) {
+ 		i++;
+@@ -1193,21 +1236,27 @@
+ 	}
+ }
+ 
+-// execute command D
+-static void delete_rule(int rule_nr)
++/*
++ * execute command D
++ */
++static void delete_rule(int begin, int end)
+ {
+-	int i, j, lentmp = 0;
++	int j, lentmp = 0, nr_deletes;
+ 	unsigned short *cnt;
+ 	struct ebt_u_entry **u_e, *u_e2;
+ 	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+-	if ( (i = check_rule_exists(rule_nr)) == -1 )
++	if ((begin = check_rule_exists(begin)) == -1 ||
++	    (end = check_rule_exists(end)) == -1)
+ 		print_error("Sorry, rule does not exist");
+ 
+-	// we're deleting a rule
++	/*
++	 * we're deleting rules
++	 */
+ 	replace.num_counters = replace.nentries;
+-	replace.nentries--;
+-	entries->nentries--;
++	nr_deletes = end - begin + 1;
++	replace.nentries -= nr_deletes;
++	entries->nentries -= nr_deletes;
+ 
+ 	if (replace.nentries) {
+ 		for (j = 0; j < replace.selected_hook; j++) {
+@@ -1217,61 +1266,75 @@
+ 			entries2 = nr_to_chain(j);
+ 			lentmp += entries2->nentries;
+ 		}
+-		lentmp += i;
+-		// +1 for CNT_END
++		lentmp += begin;
++		/*
++		 * +1 for CNT_END
++		 */
+ 		if ( !(replace.counterchanges = (unsigned short *)malloc(
+ 		   (replace.num_counters + 1) * sizeof(unsigned short))) )
+ 			print_memory();
+ 		cnt = replace.counterchanges;
+-		for (j = 0; j < lentmp; j++) {
++		for (j = 0; j < lentmp; j++, cnt++)
+ 			*cnt = CNT_NORM;
+-			cnt++;
+-		}
+-		*cnt = CNT_DEL;
+-		cnt++;
+-		for (j = 0; j < replace.num_counters - lentmp; j++) {
++		for (j = 0; j < nr_deletes; j++, cnt++)
++			*cnt = CNT_DEL;
++  
++		for (j = 0; j < replace.num_counters - lentmp - nr_deletes;
++		     j++, cnt++)
+ 			*cnt = CNT_NORM;
+-			cnt++;
+-		}
++  
+ 		*cnt = CNT_END;
+ 	}
+ 	else
+ 		replace.num_counters = 0;
+ 
+-	// go to the right position in the chain
++	/*
++	 * go to the right position in the chain
++	 */
+ 	u_e = &entries->entries;
+-	for (j = 0; j < i; j++)
++	for (j = 0; j < begin; j++)
+ 		u_e = &(*u_e)->next;
+-	// remove the rule
+-	u_e2 = *u_e;
+-	*u_e = (*u_e)->next;
+-	// free everything
+-	free_u_entry(u_e2);
+-	free(u_e2);
+-
+-	// update the counter_offset of chains behind this one
+-	i = replace.selected_hook;
++	/*
++	 * remove the rules
++	 */
++	j = nr_deletes;
++	while(j--) {
++		u_e2 = *u_e;
++		*u_e = (*u_e)->next;
++		// free everything
++		free_u_entry(u_e2);
++		free(u_e2);
++	}
++
++	/*
++	 * update the counter_offset of chains behind this one
++	 */
++	j = replace.selected_hook;
+ 	while (1) {
+-		i++;
+-		entries = nr_to_chain(i);
++		j++;
++		entries = nr_to_chain(j);
+ 		if (!entries) {
+-			if (i < NF_BR_NUMHOOKS)
++			if (j < NF_BR_NUMHOOKS)
+ 				continue;
+ 			else
+ 				break;
+-		} else
+-			entries->counter_offset--;
++		} else 
++			entries->counter_offset -= nr_deletes;
+ 	}
+ }
+ 
+-// execute command Z
++/*
++ * execute command Z
++ */
+ static void zero_counters(int zerochain)
+ {
+ 
+ 	if (zerochain == -1) {
+-		// tell main() we don't update the counters
+-		// this results in tricking the kernel to zero its counters,
+-		// naively expecting userspace to update its counters. Muahahaha
++		/*
++		 * tell main() we don't update the counters
++		 * this results in tricking the kernel to zero its counters,
++		 * naively expecting userspace to update its counters. Muahahaha
++		 */
+ 		replace.counterchanges = NULL;
+ 		replace.num_counters = 0;
+ 	} else {
+@@ -1308,38 +1371,21 @@
+ 	}
+ }
+ 
+-//  0 == success
+-//  1 == success, but for the special 'protocol' LENGTH
+-// -1 == failure
+-int name_to_number(char *name, __u16 *proto)
+-{
+-	FILE *ifp;
+-	char buffer[21], value[11], *bfr;
+-	unsigned short i;
+-
+-	if (!strcasecmp("LENGTH", name)) {
+-		*proto = 0;
+-		new_entry->bitmask |= EBT_802_3;
+-		return 1;
+-	}
+-	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+-		return -1;
+-	while (1) {
+-		if (get_a_line(buffer, value, ifp))
+-			return -1;
+-		if (strcasecmp(buffer, name))
+-			continue;
+-		fclose(ifp);
+-		i = (unsigned short) strtol(value, &bfr, 16);
+-		if (*bfr != '\0')
+-			return -1;
+-		*proto = i;
+-		return 0;
+-	}
+-	return -1;
++/*
++ * Checks the type for validity and calls getethertypebynumber()
++ */
++struct ethertypeent *parseethertypebynumber(int type)
++{
++	if (type < 1536)
++		print_error("Ethernet protocols have values >= 0x0600");
++	if (type > 0xffff)
++		print_error("Ethernet protocols have values <= 0xffff");
++	return getethertypebynumber(type);
+ }
+ 
+-// put the mac address into 6 (ETH_ALEN) bytes
++/*
++ * put the mac address into 6 (ETH_ALEN) bytes
++ */
+ int getmac_and_mask(char *from, char *to, char *mask)
+ {
+ 	char *p;
+@@ -1376,7 +1422,9 @@
+ 	return 0;
+ }
+ 
+-// executes the final_check() function for all extensions used by the rule
++/*
++ * executes the final_check() function for all extensions used by the rule
++ */
+ static void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
+ {
+ 	struct ebt_u_match_list *m_l;
+@@ -1404,7 +1452,9 @@
+ 	   entries->hook_mask, 1);
+ }
+ 
+-// used for the -X command
++/*
++ * used for the -X command
++ */
+ static void check_for_references(int chain_nr)
+ {
+ 	int i = -1, j;
+@@ -1436,13 +1486,45 @@
+ 	}
+ }
+ 
++static int parse_delete_rule(const char *argv, int *rule_nr, int *rule_nr_end)
++{
++	char *colon = strchr(argv, ':'), *buffer;
++
++	if (colon) {
++		*colon = '\0';
++		if (*(colon + 1) == '\0')
++			*rule_nr_end = -1;
++		else {
++			*rule_nr_end = strtol(colon + 1, &buffer, 10);
++			if (*buffer != '\0' || *rule_nr_end < 0)
++				return -1;
++		}
++	}
++	if (colon == argv)
++		*rule_nr = 1;
++	else {
++		*rule_nr = strtol(argv, &buffer, 10);
++		if (*buffer != '\0' || *rule_nr < 0)
++			return -1;
++	}
++	if (!colon)
++		*rule_nr_end = *rule_nr;
++	if (*rule_nr_end != -1 && *rule_nr > *rule_nr_end)
++		return -1;
++	return 0;
++}
++
++static int invert = 0;
+ int check_inverse(const char option[])
+ {
+ 	if (strcmp(option, "!") == 0) {
++		if (invert == 1)
++			print_error("double use of '!' not allowed");
+ 		optind++;
++		invert = 1;
+ 		return 1;
+ 	}
+-	return 0;
++	return invert;
+ }
+ 
+ void check_option(unsigned int *flags, unsigned int mask)
+@@ -1456,15 +1538,19 @@
+ {
+ 	if ( !(table = find_table(replace.name)) )
+ 		print_error("Bad table name");
+-	// get the kernel's information
++	/*
++	 * get the kernel's information
++	 */
+ 	if (get_table(&replace)) {
+ 		ebtables_insmod("ebtables", modprobe);
+ 		if (get_table(&replace))
+ 			print_error("The kernel doesn't support the ebtables "
+ 			"%s table", replace.name);
+ 	}
+-	// when listing a table contained in a file, we don't expect the user
+-	// to know what the table's name is
++	/*
++	 * when listing a table contained in a file, we don't demand that
++	 * the user knows the table's name
++	 */
+ 	if ( !(table = find_table(replace.name)) )
+ 		print_error("Bad table name");
+ }
+@@ -1482,15 +1568,18 @@
+ #define OPT_ZERO       0x100
+ #define OPT_LOGICALIN  0x200
+ #define OPT_LOGICALOUT 0x400
+-// the main thing
++/* the main thing */
+ int main(int argc, char *argv[])
+ {
+ 	char *buffer;
+ 	int c, i;
+-	// this special one for the -Z option (we can have -Z <this> -L <that>)
++	/*
++	 * this special one for the -Z option (we can have -Z <this> -L <that>)
++	 */
+ 	int zerochain = -1;
+ 	int policy = 0;
+-	int rule_nr = -1;// used for -[D,I] chain number
++	int rule_nr = -1; /* used for -[D,I] */
++	int rule_nr_end = -1; /* used for -I */
+ 	struct ebt_u_target *t;
+ 	struct ebt_u_match *m;
+ 	struct ebt_u_watcher *w;
+@@ -1499,36 +1588,46 @@
+ 	struct ebt_u_entries *entries;
+ 	const char *modprobe = NULL;
+ 
+-	// initialize the table name, OPT_ flags, selected hook and command
++	opterr = 0;
++
++	replace.filename = getenv(ATOMIC_ENV_VARIABLE);
++	/*
++	 * initialize the table name, OPT_ flags, selected hook and command
++	 */
+ 	strcpy(replace.name, "filter");
+ 	replace.flags = 0;
+ 	replace.selected_hook = -1;
+ 	replace.command = 'h';
+-	replace.filename = NULL;
+ 	replace.counterchanges = NULL;
+ 
+ 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ 	if (!new_entry)
+ 		print_memory();
+-	// put some sane values in our new entry
++	/*
++	 * put some sane values in our new entry
++	 */
+ 	initialize_entry(new_entry);
+ 
+-	// The scenario induced by this loop makes that:
+-	// '-t'  ,'-M' and --atomic (if specified) have to come
+-	// before '-A' and the like
+-
+-	// getopt saves the day
++	/*
++	 * The scenario induced by this loop makes that:
++	 * '-t'  ,'-M' and --atomic (if specified) have to come
++	 * before '-A' and the like
++	 */
++
++	/*
++	 * getopt saves the day
++	 */
+ 	while ((c = getopt_long(argc, argv,
+ 	   "-A:D:I:N:E:X:L::Z::F::P:Vhi:o:j:p:s:d:t:M:", ebt_options, NULL)) != -1) {
+ 		switch (c) {
+ 
+-		case 'A': // add a rule
+-		case 'D': // delete a rule
+-		case 'P': // define policy
+-		case 'I': // insert a rule
+-		case 'N': // make a user defined chain
+-		case 'E': // rename chain
+-		case 'X': // delete chain
++		case 'A': /* add a rule */
++		case 'D': /* delete a rule */
++		case 'P': /* define policy */
++		case 'I': /* insert a rule */
++		case 'N': /* make a user defined chain */
++		case 'E': /* rename chain */
++		case 'X': /* delete chain */
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+@@ -1564,7 +1663,9 @@
+ 				strcpy(cl->udc->name, optarg);
+ 				cl->udc->entries = NULL;
+ 				cl->kernel_start = NULL;
+-				// put the new chain at the end
++				/*
++				 * put the new chain at the end
++				 */
+ 				cl2 = &replace.udc;
+ 				while (*cl2)
+ 					cl2 = &((*cl2)->next);
+@@ -1596,7 +1697,9 @@
+ 
+ 				if (replace.selected_hook < NF_BR_NUMHOOKS)
+ 					print_error("You can't remove a standard chain");
+-				// if the chain is referenced, don't delete it
++				/*
++				 * if the chain is referenced, don't delete it
++				 */
+ 				check_for_references(replace.selected_hook - NF_BR_NUMHOOKS);
+ 				flush_chains();
+ 				entries = to_chain();
+@@ -1610,8 +1713,15 @@
+ 				break;
+ 			}
+ 
+-			if ( (c == 'D' && optind < argc  &&
+-			   argv[optind][0] != '-')  || c == 'I') {
++			if (c == 'D' && optind < argc &&
++			    argv[optind][0] != '-') {
++				if (parse_delete_rule(argv[optind],
++				    &rule_nr, &rule_nr_end))
++					print_error("Problem with the "
++					            "specified rule number(s)");
++				optind++;
++			}
++			if (c == 'I') {
+ 				if (optind >= argc || argv[optind][0] == '-')
+ 					print_error("No rulenr for -I"
+ 					            " specified");
+@@ -1639,9 +1749,9 @@
+ 			}
+ 			break;
+ 
+-		case 'L': // list
+-		case 'F': // flush
+-		case 'Z': // zero counters
++		case 'L': /* list */
++		case 'F': /* flush */
++		case 'Z': /* zero counters */
+ 			if (c == 'Z') {
+ 				if (replace.flags & OPT_ZERO)
+ 					print_error("Multiple commands"
+@@ -1677,24 +1787,26 @@
+ 			}
+ 			break;
+ 
+-		case 'V': // version
++		case 'V': /* version */
+ 			replace.command = 'V';
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
+ 			exit(0);
+ 
+-		case 'M': // modprobe
++		case 'M': /* modprobe */
+ 			if (replace.command != 'h')
+ 				print_error("Please put the -M option earlier");
+ 			modprobe = optarg;
+ 			break;
+ 
+-		case 'h': // help
++		case 'h': /* help */
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.command = 'h';
+-			// All other arguments should be extension names
++			/*
++			 * All other arguments should be extension names
++			 */
+ 			while (optind < argc) {
+ 				struct ebt_u_match *m;
+ 				struct ebt_u_watcher *w;
+@@ -1719,7 +1831,7 @@
+ 			}
+ 			break;
+ 
+-		case 't': // table
++		case 't': /* table */
+ 			if (replace.command != 'h')
+ 				print_error("Please put the -t option first");
+ 			check_option(&replace.flags, OPT_TABLE);
+@@ -1728,14 +1840,14 @@
+ 			strcpy(replace.name, optarg);
+ 			break;
+ 
+-		case 'i': // input interface
+-		case 2  : // logical input interface
+-		case 'o': // output interface
+-		case 3  : // logical output interface
+-		case 'j': // target
+-		case 'p': // net family protocol
+-		case 's': // source mac
+-		case 'd': // destination mac
++		case 'i': /* input interface */
++		case 2  : /* logical input interface */
++		case 'o': /* output interface */
++		case 3  : /* logical output interface */
++		case 'j': /* target */
++		case 'p': /* net family protocol */
++		case 's': /* source mac */
++		case 'd': /* destination mac */
+ 			if ((replace.flags & OPT_COMMAND) == 0)
+ 				print_error("No command specified");
+ 			if ( replace.command != 'A' &&
+@@ -1843,11 +1955,15 @@
+ 						break;
+ 					}
+ 				else {
+-					// must be an extension then
++					/*
++					 * must be an extension then
++					 */
+ 					struct ebt_u_target *t;
+ 
+ 					t = find_target(optarg);
+-					// -j standard not allowed either
++					/*
++					 * -j standard not allowed either
++					 */
+ 					if (!t || t ==
+ 					   (struct ebt_u_target *)new_entry->t)
+ 						print_error("Illegal target "
+@@ -1900,12 +2016,17 @@
+ 				            "protocol");
+ 			new_entry->ethproto = i;
+ 			if (*buffer != '\0') {
+-				if ((i = name_to_number(argv[optind - 1],
+-				   &new_entry->ethproto)) == -1)
++				struct ethertypeent *ent;
++
++				if (!strcasecmp(argv[optind - 1], "LENGTH")) {
++					new_entry->bitmask |= EBT_802_3;
++					break;
++				}
++				ent = getethertypebyname(argv[optind - 1]);
++				if (!ent)
+ 					print_error("Problem with the specified"
+ 					            " protocol");
+-				if (i == 1)
+-					new_entry->bitmask |= EBT_802_3;
++				new_entry->ethproto = ent->e_ethertype;
+ 			}
+ 			if (new_entry->ethproto < 1536 &&
+ 			   !(new_entry->bitmask & EBT_802_3))
+@@ -1944,12 +2065,11 @@
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
+-			if (replace.filename)
+-				print_error("--atomic incompatible with "
+-				   "command");
+-			replace.filename = (char *)malloc(strlen(optarg) + 1);
+-			strcpy(replace.filename, optarg);
+-			// get the information from the file
++			if (!replace.filename)
++				print_error("No atomic file specified");
++			/*
++			 * get the information from the file
++			 */
+ 			get_table(&replace);
+                         if (replace.nentries) {
+                                 replace.counterchanges = (unsigned short *)
+@@ -1958,25 +2078,36 @@
+ 					replace.counterchanges[i] = CNT_NORM;
+ 					replace.counterchanges[i] = CNT_END;
+                         }
+-			// we don't want the kernel giving us its counters, they would
+-			// overwrite the counters extracted from the file
++			/*
++			 * we don't want the kernel giving us its counters, they would
++			 * overwrite the counters extracted from the file
++			 */
+ 			replace.num_counters = 0;
+-			// make sure the table will be written to the kernel
+-			free(replace.filename);
++			/*
++			 * make sure the table will be written to the kernel
++			 * possible memory leak here
++			 */
+ 			replace.filename = NULL;
+ 			ebtables_insmod("ebtables", modprobe);
+ 			break;
+-		case 7 : // atomic-init
+-		case 10: // atomic-save
+-		case 11: // init-table
++		case 7 : /* atomic-init */
++		case 10: /* atomic-save */
++		case 11: /* init-table */
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
++			if (c != 11 && !replace.filename)
++				print_error("No atomic file specified");
+ 			replace.flags |= OPT_COMMAND;
+-			if (replace.filename)
+-				print_error("--atomic incompatible with "
+-				   "command");
+-			get_kernel_table(modprobe);
++			{
++				char *tmp = replace.filename;
++
++				tmp = replace.filename;
++				/* get the kernel table */
++				replace.filename = NULL;
++				get_kernel_table(modprobe);
++				replace.filename = tmp;
++			}
+ 			if (replace.nentries) {
+ 				replace.counterchanges = (unsigned short *)
+ 				   malloc(sizeof(unsigned short) * (replace.nentries + 1));
+@@ -1984,24 +2115,37 @@
+ 					replace.counterchanges[i] = CNT_NORM;
+ 				replace.counterchanges[i] = CNT_END;
+ 			}
+-			if (c == 11)
+-				break;
+-		case 9 : // atomic
+-			if (c == 9 && (replace.flags & OPT_COMMAND))
++			break;
++		case 9 : /* atomic */
++			if (replace.flags & OPT_COMMAND)
+ 				print_error("--atomic has to come before"
+ 				" the command");
++			/* another possible memory leak here */
+ 			replace.filename = (char *)malloc(strlen(optarg) + 1);
+ 			strcpy(replace.filename, optarg);
+ 			break;
+-
++		case 1 :
++			if (!strcmp(optarg, "!"))
++				check_inverse(optarg);
++			else
++				print_error("Bad argument : %s", optarg);
++			/*
++			 * check_inverse() did optind++
++			 */
++			optind--;
++			continue;
+ 		default:
+-			// is it a target option?
++			/*
++			 * is it a target option?
++			 */
+ 			t = (struct ebt_u_target *)new_entry->t;
+ 			if ((t->parse(c - t->option_offset, argv, argc,
+ 			   new_entry, &t->flags, &t->t)))
+ 				goto check_extension;
+ 
+-			// is it a match_option?
++			/*
++			 * is it a match_option?
++			 */
+ 			for (m = matches; m; m = m->next)
+ 				if (m->parse(c - m->option_offset, argv,
+ 				   argc, new_entry, &m->flags, &m->m))
+@@ -2013,7 +2157,9 @@
+ 				goto check_extension;
+ 			}
+ 
+-			// is it a watcher option?
++			/*
++			 * is it a watcher option?
++			 */
+ 			for (w = watchers; w; w = w->next)
+ 				if (w->parse(c-w->option_offset, argv,
+ 				   argc, new_entry, &w->flags, &w->w))
+@@ -2028,6 +2174,7 @@
+ 			   replace.command != 'D')
+ 				print_error("Extensions only for -A, -I and -D");
+ 		}
++		invert = 0;
+ 	}
+ 
+ 	if ( !table && !(table = find_table(replace.name)) )
+@@ -2037,14 +2184,20 @@
+ 	   replace.flags & OPT_ZERO )
+ 		print_error("Command -Z only allowed together with command -L");
+ 
+-	// do this after parsing everything, so we can print specific info
++	/*
++	 * do this after parsing everything, so we can print specific info
++	 */
+ 	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ 		print_help();
+ 
+-	// do the final checks
++	/*
++	 * do the final checks
++	 */
+ 	if (replace.command == 'A' || replace.command == 'I' ||
+ 	   replace.command == 'D') {
+-		// this will put the hook_mask right for the chains
++		/*
++		 * this will put the hook_mask right for the chains
++		 */
+ 		check_for_loops();
+ 		entries = to_chain();
+ 		m_l = new_entry->m_list;
+@@ -2065,8 +2218,10 @@
+ 		t->final_check(new_entry, t->t, replace.name,
+ 		   entries->hook_mask, 0);
+ 	}
+-	// so, the extensions can work with the host endian
+-	// the kernel does not have to do this ofcourse
++	/*
++	 * so, the extensions can work with the host endian
++	 * the kernel does not have to do this ofcourse
++	 */
+ 	new_entry->ethproto = htons(new_entry->ethproto);
+ 
+ 	if (replace.command == 'P') {
+@@ -2090,8 +2245,10 @@
+ 	} else if (replace.command == 'A' || replace.command == 'I') {
+ 		add_rule(rule_nr);
+ 		check_for_loops();
+-		// do the final_check(), for all entries
+-		// needed when adding a rule that has a chain target
++		/*
++		 * do the final_check(), for all entries
++		 * needed when adding a rule that has a chain target
++		 */
+ 		i = -1;
+ 		while (1) {
+ 			struct ebt_u_entry *e;
+@@ -2106,17 +2263,24 @@
+ 			}
+ 			e = entries->entries;
+ 			while (e) {
+-				// userspace extensions use host endian
++				/*
++				 * userspace extensions use host endian
++				 */
+ 				e->ethproto = ntohs(e->ethproto);
+ 				do_final_checks(e, entries);
+ 				e->ethproto = htons(e->ethproto);
+ 				e = e->next;
+ 			}
+ 		}
+-	} else if (replace.command == 'D')
+-		delete_rule(rule_nr);
+-	// commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+-	// --init-table fall through
++	} else if (replace.command == 'D') {
++		if (rule_nr != -1 && rule_nr_end == -1)
++			rule_nr_end = entries->nentries;
++		delete_rule(rule_nr, rule_nr_end);
++	}
++	/*
++	 * commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
++	 * --init-table fall through
++	 */
+ 
+ 	if (table->check)
+ 		table->check(&replace);
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0.2/getethertype.c	Sat Dec  7 13:28:57 2002
+@@ -0,0 +1,162 @@
++/*
++* getethertype.c
++*
++* This file was part of the NYS Library.
++*
++** The NYS Library is free software; you can redistribute it and/or
++** modify it under the terms of the GNU Library General Public License as
++** published by the Free Software Foundation; either version 2 of the
++** License, or (at your option) any later version.
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the Free Software
++* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++*/
++
++/********************************************************************
++* Description: Ethertype name service switch and the ethertypes 
++* database access functions
++* Author: Nick Fedchik <fnm@ukrsat.com>
++* Checker: Bart De Schuymer <bdschuym@pandora.be>
++* Origin: uClibc-0.9.16/libc/inet/getproto.c
++* Created at: Mon Nov 11 12:20:11 EET 2002
++********************************************************************/
++
++
++#include <ctype.h>
++#include <features.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <netinet/ether.h>
++#include <net/ethernet.h>
++
++#include "ethernetdb.h"
++
++#define	MAXALIASES	35
++
++static FILE *etherf = NULL;
++static char line[BUFSIZ + 1];
++static struct ethertypeent et_ent;
++static char *ethertype_aliases[MAXALIASES];
++static int ethertype_stayopen;
++
++void setethertypeent(int f)
++{
++	if (etherf == NULL)
++		etherf = fopen(_PATH_ETHERTYPES, "r");
++	else
++		rewind(etherf);
++	ethertype_stayopen |= f;
++}
++
++void endethertypeent(void)
++{
++	if (etherf) {
++		fclose(etherf);
++		etherf = NULL;
++	}
++	ethertype_stayopen = 0;
++}
++
++struct ethertypeent *getethertypeent(void)
++{
++	char *e;
++	char *endptr;
++	register char *cp, **q;
++
++	if (etherf == NULL
++	    && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
++		return (NULL);
++	}
++
++again:
++	if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
++		return (NULL);
++	}
++	if (*e == '#')
++		goto again;
++	cp = strpbrk(e, "#\n");
++	if (cp == NULL)
++		goto again;
++	*cp = '\0';
++	et_ent.e_name = e;
++	cp = strpbrk(e, " \t");
++	if (cp == NULL)
++		goto again;
++	*cp++ = '\0';
++	while (*cp == ' ' || *cp == '\t')
++		cp++;
++	e = strpbrk(cp, " \t");
++	if (e != NULL)
++		*e++ = '\0';
++// Check point
++	et_ent.e_ethertype = strtol(cp, &endptr, 16);
++	if (*endptr != '\0'
++	    || (et_ent.e_ethertype < ETH_ZLEN
++		|| et_ent.e_ethertype > 0xFFFF))
++		goto again;	// Skip invalid etherproto type entry
++	q = et_ent.e_aliases = ethertype_aliases;
++	if (e != NULL) {
++		cp = e;
++		while (cp && *cp) {
++			if (*cp == ' ' || *cp == '\t') {
++				cp++;
++				continue;
++			}
++			if (q < &ethertype_aliases[MAXALIASES - 1])
++				*q++ = cp;
++			cp = strpbrk(cp, " \t");
++			if (cp != NULL)
++				*cp++ = '\0';
++		}
++	}
++	*q = NULL;
++	return (&et_ent);
++}
++
++
++struct ethertypeent *getethertypebyname(const char *name)
++{
++	register struct ethertypeent *e;
++	register char **cp;
++
++	setethertypeent(ethertype_stayopen);
++	while ((e = getethertypeent()) != NULL) {
++		if (strcasecmp(e->e_name, name) == 0)
++			break;
++		for (cp = e->e_aliases; *cp != 0; cp++)
++			if (strcasecmp(*cp, name) == 0)
++				goto found;
++	}
++found:
++	if (!ethertype_stayopen)
++		endethertypeent();
++	return (e);
++}
++
++struct ethertypeent *getethertypebynumber(int type)
++{
++	register struct ethertypeent *e;
++
++	setethertypeent(ethertype_stayopen);
++	while ((e = getethertypeent()) != NULL)
++		if (e->e_ethertype == type)
++			break;
++	if (!ethertype_stayopen)
++		endethertypeent();
++	return (e);
++}
+--- ebtables-v2.0.1/extensions/ebt_arp.c	Thu Aug 29 18:48:36 2002
++++ ebtables-v2.0.2/extensions/ebt_arp.c	Fri Nov 22 20:43:47 2002
+@@ -3,6 +3,7 @@
+ #include <stdlib.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
++#include "../include/ethernetdb.h"
+ #include <linux/netfilter_bridge/ebt_arp.h>
+ 
+ #define ARP_OPCODE '1'
+@@ -52,7 +53,7 @@
+ 		printf("%d = %s\n", i + 1, opcodes[i]);
+ 	printf(
+ " hardware type string: 1 = Ethernet\n"
+-" protocol type string: see /etc/ethertypes\n");
++" protocol type string: see "_PATH_ETHERTYPES"\n");
+ }
+ 
+ static void init(struct ebt_entry_match *match)
+@@ -133,9 +134,14 @@
+ 			print_error("Missing ARP protocol type argument");
+ 		i = strtol(argv[optind - 1], &end, 16);
+ 		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+-			if (name_to_number (argv[optind - 1], &proto) == -1)
++			struct ethertypeent *ent;
++
++			ent = getethertypebyname(argv[optind - 1]);
++			if (!ent)
+ 				print_error("Problem with specified ARP "
+ 				            "protocol type");
++			proto = ent->e_ethertype;
++
+ 		} else
+ 			proto = i;
+ 		arpinfo->ptype = htons(proto);
+@@ -190,7 +196,6 @@
+ {
+ 	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+ 	int i;
+-	char name[21];
+ 
+ 	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+ 		int opcode = ntohs(arpinfo->opcode);
+@@ -209,13 +214,16 @@
+ 		printf("%d ", ntohs(arpinfo->htype));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_PTYPE) {
++		struct ethertypeent *ent;
++
+ 		printf("--arp-ptype ");
+ 		if (arpinfo->invflags & EBT_ARP_PTYPE)
+ 			printf("! ");
+-		if (number_to_name(ntohs(arpinfo->ptype), name))
++		ent = getethertypebynumber(ntohs(arpinfo->ptype));
++		if (!ent)
+ 			printf("0x%x ", ntohs(arpinfo->ptype));
+ 		else
+-			printf("%s ", name);
++			printf("%s ", ent->e_name);
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+ 		printf("--arp-ip-src ");
+--- ebtables-v2.0.1/extensions/ebt_vlan.c	Thu Aug 29 18:48:36 2002
++++ ebtables-v2.0.2/extensions/ebt_vlan.c	Sat Dec  7 13:29:16 2002
+@@ -34,39 +34,73 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <getopt.h>
++#include <ctype.h>
+ #include "../include/ebtables_u.h"
++#include "../include/ethernetdb.h"
+ #include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_ether.h>
++
+ 
+ #define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_
+ #define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_
+ #define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "! " : ""
++#define CHECK_IF_MISSING_VALUE if (optind > argc) print_error ("Missing %s value", opts[c].name);
++#define CHECK_INV_FLAG(_INDEX_) if (check_inverse (optarg)) vlaninfo->invflags |= _INDEX_;
++#define CHECK_RANGE(_RANGE_) if (_RANGE_) print_error ("Invalid %s range", opts[c].name);
++
++#define NAME_VLAN_ID    "id"
++#define NAME_VLAN_PRIO  "prio"
++#define NAME_VLAN_ENCAP "encap"
+ 
+ #define VLAN_ID    0
+ #define VLAN_PRIO  1
+ #define VLAN_ENCAP 2
++
+ static struct option opts[] = {
+-	{"vlan-id", required_argument, NULL, VLAN_ID},
+-	{"vlan-prio", required_argument, NULL, VLAN_PRIO},
+-	{"vlan-encap", required_argument, NULL, VLAN_ENCAP},
++	{EBT_VLAN_MATCH "-" NAME_VLAN_ID, required_argument, NULL,
++	 VLAN_ID},
++	{EBT_VLAN_MATCH "-" NAME_VLAN_PRIO, required_argument, NULL,
++	 VLAN_PRIO},
++	{EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP, required_argument, NULL,
++	 VLAN_ENCAP},
+ 	{NULL}
+ };
+ 
++/*
++ * option inverse flags definition 
++ */
++#define OPT_VLAN_ID     0x01
++#define OPT_VLAN_PRIO   0x02
++#define OPT_VLAN_ENCAP  0x04
++#define OPT_VLAN_FLAGS	(OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP)
++
++struct ethertypeent *ethent;
+ 
+ /*
+- * Print out local help by "ebtables -h vlan" 
++ * Print out local help by "ebtables -h <match name>" 
+  */
+-static void print_help ()
++
++static void print_help()
+ {
+-	printf ("802.1Q VLAN extension options:\n"
+-		"--vlan-id [!] id        : VLAN-tagged frame identifier, 0,1-4094 (integer)\n"
+-		"--vlan-prio [!] prio    : Priority-tagged frame user_priority, 0-7 (integer)\n"
+-		"--vlan-encap [!] proto  : Encapsulated protocol (hexadecimal)\n");
++#define HELP_TITLE "802.1Q VLAN extension"
++
++	printf(HELP_TITLE " options:\n");
++	printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ID " %s" NAME_VLAN_ID
++	       " : VLAN-tagged frame identifier, 0,1-4096 (integer), default 1\n",
++	       OPT_VLAN_FLAGS & OPT_VLAN_ID ? "[!] " : "");
++	printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_PRIO " %s" NAME_VLAN_PRIO
++	       " : Priority-tagged frame user_priority, 0-7 (integer), default 0\n",
++	       OPT_VLAN_FLAGS & OPT_VLAN_PRIO ? "[!] " : "");
++	printf("--" EBT_VLAN_MATCH "-" NAME_VLAN_ENCAP " %s"
++	       NAME_VLAN_ENCAP
++	       " : Encapsulated frame type (hexadecimal), default IP (0800)\n",
++	       OPT_VLAN_FLAGS & OPT_VLAN_ENCAP ? "[!] " : "");
+ }
+ 
+ /*
+  * Initialization function 
+  */
+-static void init (struct ebt_entry_match *match)
++static void init(struct ebt_entry_match *match)
+ {
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) match->data;
+@@ -80,131 +114,72 @@
+ 	vlaninfo->bitmask = 0;
+ }
+ 
+-/*
+- * option flags definition 
+- */
+-#define OPT_VLAN_ID     0x01
+-#define OPT_VLAN_PRIO   0x02
+-#define OPT_VLAN_ENCAP  0x04
+ 
+ /*
+  * Parse passed arguments values (ranges, flags, etc...)
+  * int c - parameter number from static struct option opts[]
+  * int argc - total amout of arguments (std argc value)
+- * 
++ * int argv - arguments (std argv value)
++ * const struct ebt_u_entry *entry - default ebtables entry set
++ * unsigned int *flags -
++ * struct ebt_entry_match **match - 
+  */
+ static int
+-parse (int c,
+-       char **argv,
+-       int argc,
+-       const struct ebt_u_entry *entry,
+-       unsigned int *flags, struct ebt_entry_match **match)
++parse(int c,
++      char **argv,
++      int argc,
++      const struct ebt_u_entry *entry,
++      unsigned int *flags, struct ebt_entry_match **match)
+ {
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) (*match)->data;
+-	unsigned long i;
+ 	char *end;
+-	uint16_t encap;
++	struct ebt_vlan_info local;
++
+ 	switch (c) {
+ 	case VLAN_ID:
+-		/*
+-		 * ebtables.c:check_option(unsigned int *flags, unsigned int mask)
+-		 * checking for multiple usage of same option 
+-		 */
+-		check_option (flags, OPT_VLAN_ID);
+-		/*
+-		 * Check If we got inversed arg for vlan-id option,
+-		 * otherwise unset inversion flag 
+-		 */
+-		if (check_inverse (optarg))
+-			vlaninfo->invflags |= EBT_VLAN_ID;
+-		/*
+-		 * Check arg value presence
+-		 */
+-		if (optind > argc)
+-			print_error ("Missing VLAN ID argument value");
+-		/*
+-		 * Convert argv to long int,
+-		 * set *end to end of argv string, 
+-		 * base set 10 for decimal only
+-		 */
+-		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
+-		/*
+-		 * Check arg val range
+-		 */
+-		if (i > 4094 || *end != '\0')
+-			print_error
+-			    ("Specified VLAN ID is out of range (0-4094)");
+-		/*
+-		 * Set up parameter value
+-		 */
+-		vlaninfo->id = i;
+-		/*
+-		 * Set up parameter presence flag 
+-		 */
+-		SET_BITMASK (EBT_VLAN_ID);
++		check_option(flags, OPT_VLAN_ID);
++		CHECK_INV_FLAG(EBT_VLAN_ID);
++		CHECK_IF_MISSING_VALUE;
++		(unsigned short) local.id =
++		    strtoul(argv[optind - 1], &end, 10);
++		CHECK_RANGE(local.id > 4094 || *end != '\0');
++		vlaninfo->id = local.id;
++		SET_BITMASK(EBT_VLAN_ID);
+ 		break;
+ 
+ 	case VLAN_PRIO:
+-		check_option (flags, OPT_VLAN_PRIO);
+-		if (check_inverse (optarg))
+-			vlaninfo->invflags |= EBT_VLAN_PRIO;
+-		if (optind > argc)
+-			print_error
+-			    ("Missing user_priority argument value");
+-		/*
+-		 * Convert argv to long int,
+-		 * set *end to end of argv string, 
+-		 * base set 10 for decimal only 
+-		 */
+-		(unsigned char) i = strtol (argv[optind - 1], &end, 10);
+-		/*
+-		 * Check arg val range 
+-		 */
+-		if (i >= 8 || *end != '\0')
+-			print_error
+-			    ("Specified user_priority is out of range (0-7)");
+-		/*
+-		 * Set up parameter value 
+-		 */
+-		vlaninfo->prio = i;
+-		/*
+-		 * Set up parameter presence flag 
+-		 */
+-		SET_BITMASK (EBT_VLAN_PRIO);
++		check_option(flags, OPT_VLAN_PRIO);
++		CHECK_INV_FLAG(EBT_VLAN_PRIO);
++		CHECK_IF_MISSING_VALUE;
++		(unsigned char) local.prio =
++		    strtoul(argv[optind - 1], &end, 10);
++		CHECK_RANGE(local.prio >= 8 || *end != '\0');
++		vlaninfo->prio = local.prio;
++		SET_BITMASK(EBT_VLAN_PRIO);
+ 		break;
+ 
+ 	case VLAN_ENCAP:
+-		check_option (flags, OPT_VLAN_ENCAP);
+-		if (check_inverse (optarg))
+-			vlaninfo->invflags |= EBT_VLAN_ENCAP;
+-		if (optind > argc)
+-			print_error
+-			    ("Missing encapsulated frame type argument value");
+-		/*
+-		 * Parameter can be decimal, hexadecimal, or string.
+-		 * Check arg val range (still raw area)
+-		 */
+-		(unsigned short) encap = strtol (argv[optind - 1], &end, 16);
+-		if (*end == '\0' && (encap < ETH_ZLEN || encap > 0xFFFF))
+-			print_error
+-			    ("Specified encapsulated frame type is out of range");
+-		if (*end != '\0')
+-			if (name_to_number (argv[optind - 1], &encap) == -1)
+-				print_error
+-				    ("Problem with the specified encapsulated"
+-				     "protocol");
+-		/*
+-		 * Set up parameter value (network notation)
+-		 */
+-		vlaninfo->encap = htons (encap);
+-		/*
+-		 * Set up parameter presence flag 
+-		 */
+-		SET_BITMASK (EBT_VLAN_ENCAP);
++		check_option(flags, OPT_VLAN_ENCAP);
++		CHECK_INV_FLAG(EBT_VLAN_ENCAP);
++		CHECK_IF_MISSING_VALUE;
++		(unsigned short) local.encap =
++		    strtoul(argv[optind - 1], &end, 16);
++		if (*end != '\0') {
++			ethent = getethertypebyname(argv[optind - 1]);
++			if (ethent == NULL)
++				print_error("Unknown %s encap",
++					    opts[c].name);
++			local.encap = ethent->e_ethertype;
++		}
++		CHECK_RANGE(local.encap < ETH_ZLEN);
++		vlaninfo->encap = htons(local.encap);
++		SET_BITMASK(EBT_VLAN_ENCAP);
+ 		break;
++
+ 	default:
+ 		return 0;
++
+ 	}
+ 	return 1;
+ }
+@@ -213,9 +188,9 @@
+  * Final check - logical conditions
+  */
+ static void
+-final_check (const struct ebt_u_entry *entry,
+-	     const struct ebt_entry_match *match,
+-	     const char *name, unsigned int hookmask, unsigned int time)
++final_check(const struct ebt_u_entry *entry,
++	    const struct ebt_entry_match *match,
++	    const char *name, unsigned int hookmask, unsigned int time)
+ {
+ 
+ 	struct ebt_vlan_info *vlaninfo =
+@@ -223,16 +198,25 @@
+ 	/*
+ 	 * Specified proto isn't 802.1Q?
+ 	 */
+-	if (entry->ethproto != ETH_P_8021Q ||
+-	    entry->invflags & EBT_IPROTO)
++	if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO)
+ 		print_error
+ 		    ("For use 802.1Q extension the protocol must be specified as 802_1Q");
+ 	/*
++	 * Check if specified vlan-encap=0x8100 (802.1Q Frame) 
++	 * when vlan-encap specified.
++	 */
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		if (vlaninfo->encap == htons(0x8100))
++			print_error
++			    ("Encapsulated frame type can not be 802.1Q (0x8100)");
++	}
++
++	/*
+ 	 * Check if specified vlan-id=0 (priority-tagged frame condition) 
+ 	 * when vlan-prio was specified.
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_PRIO)) {
+-		if (vlaninfo->id && GET_BITMASK (EBT_VLAN_ID))
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		if (vlaninfo->id && GET_BITMASK(EBT_VLAN_ID))
+ 			print_error
+ 			    ("For use user_priority the specified vlan-id must be 0");
+ 	}
+@@ -242,49 +226,46 @@
+  * Print line when listing rules by ebtables -L 
+  */
+ static void
+-print (const struct ebt_u_entry *entry,
+-       const struct ebt_entry_match *match)
++print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match)
+ {
+ 	struct ebt_vlan_info *vlaninfo =
+ 	    (struct ebt_vlan_info *) match->data;
+ 
+-	char ethertype_name[21];
+ 	/*
+ 	 * Print VLAN ID if they are specified 
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_ID)) {
+-		printf ("--%s %s%d ",
+-			opts[VLAN_ID].name,
+-			INV_FLAG (EBT_VLAN_ID), vlaninfo->id);
++	if (GET_BITMASK(EBT_VLAN_ID)) {
++		printf("--%s %s%d ",
++		       opts[VLAN_ID].name,
++		       INV_FLAG(EBT_VLAN_ID), vlaninfo->id);
+ 	}
+ 	/*
+ 	 * Print user priority if they are specified 
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_PRIO)) {
+-		printf ("--%s %s%d ",
+-			opts[VLAN_PRIO].name,
+-			INV_FLAG (EBT_VLAN_PRIO), vlaninfo->prio);
++	if (GET_BITMASK(EBT_VLAN_PRIO)) {
++		printf("--%s %s%d ",
++		       opts[VLAN_PRIO].name,
++		       INV_FLAG(EBT_VLAN_PRIO), vlaninfo->prio);
+ 	}
+ 	/*
+ 	 * Print encapsulated frame type if they are specified 
+ 	 */
+-	if (GET_BITMASK (EBT_VLAN_ENCAP)) {
+-		printf ("--%s %s",
+-			opts[VLAN_ENCAP].name, INV_FLAG (EBT_VLAN_ENCAP));
+-		bzero (ethertype_name, 21);
+-		if (!number_to_name
+-		    (ntohs (vlaninfo->encap), ethertype_name)) {
+-			printf ("%s ", ethertype_name);
++	if (GET_BITMASK(EBT_VLAN_ENCAP)) {
++		printf("--%s %s",
++		       opts[VLAN_ENCAP].name, INV_FLAG(EBT_VLAN_ENCAP));
++		ethent = getethertypebynumber(ntohs(vlaninfo->encap));
++		if (ethent != NULL) {
++			printf("%s ", ethent->e_name);
+ 		} else {
+-			printf ("%2.4X ", ntohs (vlaninfo->encap));
++			printf("%4.4X ", ntohs(vlaninfo->encap));
+ 		}
+ 	}
+ }
+ 
+ 
+ static int
+-compare (const struct ebt_entry_match *vlan1,
+-	 const struct ebt_entry_match *vlan2)
++compare(const struct ebt_entry_match *vlan1,
++	const struct ebt_entry_match *vlan2)
+ {
+ 	struct ebt_vlan_info *vlaninfo1 =
+ 	    (struct ebt_vlan_info *) vlan1->data;
+@@ -321,12 +302,13 @@
+ 		if (vlaninfo1->encap != vlaninfo2->encap)
+ 			return 0;
+ 	};
++
+ 	return 1;
+ }
+ 
+ static struct ebt_u_match vlan_match = {
+ 	EBT_VLAN_MATCH,
+-	sizeof (struct ebt_vlan_info),
++	sizeof(struct ebt_vlan_info),
+ 	print_help,
+ 	init,
+ 	parse,
+@@ -336,8 +318,8 @@
+ 	opts
+ };
+ 
+-static void _init (void) __attribute__ ((constructor));
+-static void _init (void)
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
+ {
+-	register_match (&vlan_match);
++	register_match(&vlan_match);
+ }
+--- ebtables-v2.0.1/extensions/Makefile	Wed Jul 24 10:36:48 2002
++++ ebtables-v2.0.2/extensions/Makefile	Fri Nov 22 20:44:37 2002
+@@ -6,7 +6,8 @@
+ EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+ 
+ extensions/ebt_%.o: extensions/ebt_%.c include/ebtables_u.h
+-	$(CC) $(CFLAGS) -c -o $@ $<
++	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
++
+ extensions/ebtable_%.o: extensions/ebtable_%.c
+-	$(CC) $(CFLAGS) -c -o $@ $<
++	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
+ 
+--- ebtables-v2.0.1/ChangeLog	Fri Aug 30 22:33:36 2002
++++ ebtables-v2.0.2/ChangeLog	Tue Dec  3 23:00:45 2002
+@@ -1,3 +1,20 @@
++20021203
++	* changed the way to use the atomic operations. It's now possible
++	  to use the EBTABLES_ATOMIC_FILE environment variable, so it's no
++	  longer necessary to explicitly state the file name. See the man.
++20021120
++	* changed the way of compiling. New releases will now contain their
++	  own set of kernel includes. No more copying of kernel includes to
++	  /usr/include/linux
++	* added getethertype.c (Nick) and use it. Removed name_to_number()
++	  and number_to_name().
++20021106
++	* added possibility to specify a rule number interval when deleting
++	  rules
++20021102
++	* added ! - option possibility, which is equivalent to - ! option
++20021102
++	* since last entry: added byte counters and udp/tcp port matching
+ 20020830
+ 	* updated the kernel files for 2.4.20-pre5 and 2.5.32
+ 	* last big cleanup of kernel and userspace code just finished
+--- ebtables-v2.0.1/ebtables.8	Thu Oct 17 23:20:57 2002
++++ ebtables-v2.0.2/ebtables.8	Sat Dec  7 13:42:58 2002
+@@ -1,6 +1,6 @@
+-.TH EBTABLES 8  "11 August 2002"
++.TH EBTABLES 8  "03 December 2002"
+ .\"
+-.\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
++.\" Man page written by Bart De Schuymer <bdschuym@pandora.be>
+ .\" It is based on the iptables man page.
+ .\"
+ .\" Iptables page by Herve Eychenne March 2000.
+@@ -35,11 +35,11 @@
+ .br
+ .BR "ebtables --init-table"
+ .br
+-.BR "ebtables --atomic-init " file
++.BR "ebtables --atomic-init "
+ .br
+-.BR "ebtables --atomic-save " file
++.BR "ebtables --atomic-save "
+ .br
+-.BR "ebtables --atomic-commit " file
++.BR "ebtables --atomic-commit "
+ .br
+ .SH DESCRIPTION
+ .B ebtables
+@@ -133,9 +133,10 @@
+ Append a rule to the end of the selected chain.
+ .TP
+ .B "-D, --delete"
+-Delete the specified rule from the selected chain. There are two versions
+-of this command. A rule number (starting at 1) or the complete rule can be
+-specified.
++Delete the specified rule from the selected chain. There are two ways to
++use this command. The first is by specifying an interval of rule numbers
++to delete, syntax: start_nr[:end_nr]. The second usage is by specifying
++the complete rule as it would have been specified when it was added.
+ .TP
+ .B "-I, --insert"
+ Insert the specified rule into the selected chain at the specified rule number (1 meaning
+@@ -178,10 +179,8 @@
+ This will cause the rule counters to be printed on the screen before they are put on zero.
+ .TP
+ .B "-P, --policy"
+-Set the policy for the chain to the given target. The policy is either
+-.B ACCEPT
+-, either
+-.BR DROP .
++Set the policy for the chain to the given target. The policy can be
++.BR ACCEPT ", " DROP " or " RETURN .
+ .TP
+ .B "-N, --new-chain"
+ Create a new user-defined chain by the given name. The number of
+@@ -202,26 +201,34 @@
+ .B "--atomic-init"
+ Copy the kernel's initial data of the table to the specified
+ file. This can be used as the first action, after which rules are added
+-to the file.
++to the file. The file can be specified using the
++.B --atomic-file
++option or through the
++.IR EBTABLES_ATOMIC_FILE " environment variable."
+ .TP
+ .B "--atomic-save"
+ Copy the kernel's current data of the table to the specified
+ file. This can be used as the first action, after which rules are added
+-to the file.
++to the file. The file can be specified using the
++.B --atomic-file
++option or through the
++.IR EBTABLES_ATOMIC_FILE " environment variable."
+ .TP
+ .B "--atomic-commit"
+ Replace the kernel table data with the data contained in the specified
+ file. This is a useful command that allows you to put all your rules of a
+ certain table into the kernel at once, saving the kernel a lot of precious
+-time. The file which contains the table data is constructed by using
+-either the
++time and allowing atomic updates of the tables. The file which contains
++the table data is constructed by using either the
+ .B "--atomic-init"
+ or the
+ .B "--atomic-save"
+ command to get a starting file. After that, using the
+-.B "--atomic"
+-option when constructing rules allows you to extend the file and build up
+-the complete wanted table.
++.B "--atomic-file"
++option when constructing rules or setting the
++.IR EBTABLES_ATOMIC_FILE " environment variable"
++allows you to extend the file and build the complete table before
++commiting it to the kernel.
+ .SS
+ PARAMETERS
+ The following parameters make up a rule specification (as used in the add
+@@ -334,11 +341,13 @@
+ .BR "TARGET EXTENSIONS" ")"
+ or a user defined chain name.
+ .TP
+-.B --atomic file
++.B --atomic-file file
+ Let the command operate on the specified file. The data of the table to
+ operate on will be extracted from the file and the result of the operation
+ will be saved back into the file. If specified, this option should come
+-before the command specification.
++before the command specification. An alternative that should be preferred,
++is setting the
++.BR EBTABLES_ATOMIC_FILE "environment variable."
+ .TP
+ .B -M, --modprobe program
+ When talking to the kernel, use this program to try to automatically load
+@@ -560,9 +569,11 @@
+ .br
+ .SH FILES
+ .I /etc/ethertypes
++.SH ENVIRONMENT VARIABLES
++.I EBTABLES_ATOMIC_FILE
+ .SH BUGS
+ This won't work on an architecture with a user32/kernel64 situation like the Sparc64.
+ .SH AUTHOR
+-.IR "" "Bart De Schuymer <" bart.de.schuymer@pandora.be >
++.IR "" "Bart De Schuymer <" bdschuym@pandora.be >
+ .SH SEE ALSO
+ .BR iptables "(8), " brctl (8)
+--- ebtables-v2.0.1/ethertypes	Sun Aug 11 18:49:14 2002
++++ ebtables-v2.0.2/ethertypes	Wed Nov 20 20:44:45 2002
+@@ -1,32 +1,37 @@
+-# all whitespace is ignored
+-# comment lines must have a '#' as the first character
+-# all protocol numbers are in hexadecimal form
+-# maximum namesize = 20 characters
+-# always put tabs or spaces between the name and the protocol number
+-# anything on a line after the protocol number is ignored
+-# programs using this file should not be case sensitive
+-IPv4	 	0800
++#
++# Ethernet frame types
++#		This file describes some of the various Ethernet
++#		protocol types that are used on Ethernet networks.
++#
++# This list could be found on:
++#         http://www.iana.org/assignments/ethernet-numbers
++#
++# <name>    <hexnumber> <alias1>...<alias35> #Comment
++#
++IPv4	 	0800  	ip ip4 		# Internet IP (IPv4)
+ X25		0805
+-ARP		0806
+-802_1Q		8100	802.1Q Virtual LAN tagged frame
+-IPX		8137
+-IPv6		86DD
+-NetBEUI		8191
+-BPQ		08FF	G8BPQ AX.25 Ethernet Packet
+-DEC		6000	DEC Assigned proto
+-DNA_DL		6001	DEC DNA Dump/Load
+-DNA_RC		6002	DEC DNA Remote Console
+-DNA_RT		6003	DEC DNA Routing
+-LAT		6004	DEC LAT
+-DIAG		6005	DEC Diagnostics
+-CUST		6006	DEC Customer use
+-SCA		6007	DEC Systems Comms Arch
+-RARP		8035	Reverse Addr Res packet
+-ATALK		809B	Appletalk DDP
+-AARP		80F3	Appletalk AARP
+-IPX		8137	IPX over DIX
+-PPP_DISC	8863	PPPoE discovery messages
+-PPP_SES		8864	PPPoE session messages
+-ATMMPOA		884C	MultiProtocol over ATM
+-ATMFATE		8884	Frame-based ATM Transport over Ethernet
+-LOOP		9000
++ARP		0806	ether-arp	#
++FR_ARP		0808    		# Frame Relay ARP        [RFC1701]
++BPQ		08FF			# G8BPQ AX.25 Ethernet Packet
++DEC		6000			# DEC Assigned proto
++DNA_DL		6001			# DEC DNA Dump/Load
++DNA_RC		6002			# DEC DNA Remote Console
++DNA_RT		6003			# DEC DNA Routing
++LAT		6004			# DEC LAT
++DIAG		6005			# DEC Diagnostics
++CUST		6006			# DEC Customer use
++SCA		6007			# DEC Systems Comms Arch
++TEB		6558             	# Trans Ether Bridging   [RFC1701]
++RAW_FR  	6559                   	# Raw Frame Relay        [RFC1701]
++AARP		80F3			# Appletalk AARP
++ATALK		809B                  	# Appletalk
++802_1Q		8100	8021q 1q 802.1q	dot1q # 802.1Q Virtual LAN tagged frame
++IPX		8137			# Novell IPX
++NetBEUI		8191			# NetBEUI
++IPv6		86DD	ip6 		# IP version 6
++PPP		880B                    # PPP
++ATMMPOA		884C			# MultiProtocol over ATM
++PPP_DISC	8863			# PPPoE discovery messages
++PPP_SES		8864			# PPPoE session messages
++ATMFATE		8884			# Frame-based ATM Transport over Ethernet
++LOOP		9000	loopback 	# loop proto
+--- ebtables-v2.0.1/include/ebtables_u.h	Thu Aug 29 18:52:36 2002
++++ ebtables-v2.0.2/include/ebtables_u.h	Wed Nov 20 22:05:39 2002
+@@ -198,14 +198,12 @@
+ struct ebt_u_table *find_table(char *name);
+ void deliver_counters(struct ebt_u_replace *repl);
+ void deliver_table(struct ebt_u_replace *repl);
+-int name_to_number(char *name, uint16_t *proto);
+-int number_to_name(unsigned short proto, char *name);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
+ void __print_bug(char *file, int line, char *format, ...);
+ #define print_bug(format, args...) \
+    __print_bug(__FILE__, __LINE__, format, ##args)
+-#define print_error(format, args...) {printf(format".\n", ##args); exit(-1);}
++#define print_error(format,args...) {printf(format".\n",##args); exit(-1);}
+ #define print_memory() {printf("Ebtables: " __FILE__ " " __FUNCTION__ \
+    " %d :Out of memory.\n", __LINE__); exit(-1);}
+ 
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0.2/include/ethernetdb.h	Fri Nov 22 20:44:03 2002
+@@ -0,0 +1,58 @@
++/*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of the GNU General Public License as published by
++* the Free Software Foundation; either version 2 of the License, or
++* (at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the Free Software
++* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++*/
++
++/* All data returned by the network data base library are supplied in
++   host order and returned in network order (suitable for use in
++   system calls).  */
++
++#ifndef	_ETHERNETDB_H
++#define	_ETHERNETDB_H	1
++
++#include <features.h>
++#include <netinet/in.h>
++#include <stdint.h>
++
++/* Absolute file name for network data base files.  */
++#ifndef	_PATH_ETHERTYPES
++#define	_PATH_ETHERTYPES	"/etc/ethertypes"
++#endif				/* _PATH_ETHERTYPES */
++
++struct ethertypeent {
++	char *e_name;		/* Official ethernet type name.  */
++	char **e_aliases;	/* Alias list.  */
++	int e_ethertype;	/* Ethernet type number.  */
++};
++
++/* Open ethertype data base files and mark them as staying open even
++   after a later search if STAY_OPEN is non-zero.  */
++extern void setethertypeent(int __stay_open) __THROW;
++
++/* Close ethertype data base files and clear `stay open' flag.  */
++extern void endethertypeent(void) __THROW;
++
++/* Get next entry from ethertype data base file.  Open data base if
++   necessary.  */
++extern struct ethertypeent *getethertypeent(void) __THROW;
++
++/* Return entry from ethertype data base for network with NAME.  */
++extern struct ethertypeent *getethertypebyname(__const char *__name)
++    __THROW;
++
++/* Return entry from ethertype data base which number is PROTO.  */
++extern struct ethertypeent *getethertypebynumber(int __ethertype) __THROW;
++
++
++#endif				/* ethernetdb.h */
diff --git a/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.3.001.diff b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.3.001.diff
new file mode 100644
index 0000000..8973955
--- /dev/null
+++ b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.3.001.diff
@@ -0,0 +1,1796 @@
+--- ebtables-v2.0.2/Makefile	Sat Dec  7 13:09:40 2002
++++ ebtables-v2.0.3/Makefile	Tue Apr  1 22:12:30 2003
+@@ -1,8 +1,8 @@
+ # ebtables Makefile
+ 
+ PROGNAME:=ebtables
+-PROGVERSION:=2.0.2
+-PROGDATE:=December\ 2002
++PROGVERSION:=2.0.3
++PROGDATE:=April\ 2003
+ 
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+@@ -12,20 +12,13 @@
+ 
+ OBJECTS:=getethertype.o ebtables.o communication.o $(EXT_OBJS)
+ 
+-# Use the option NONSTANDARD=y when you don't want to use the kernel includes
+-# that are included in this package. You should set KERNEL_INCLUDES to
+-# the right directory (eg /usr/src/linux/include).
+-# You should only need this when compiling the CVS or when adding new code.
+-ifeq ($(NONSTANDARD), y)
+-KERNEL_INCLUDES?=/usr/include/
+-else
+-KERNEL_INCLUDES:=include/
+-endif
+-
+-ifeq ($(ETHERTYPESPATH),)
+-ETHERTYPESPATH:=/etc/
+-endif
+-ETHERTYPESFILE:=$(ETHERTYPESPATH)ethertypes
++KERNEL_INCLUDES?=include/
++
++ETHERTYPESPATH?=/etc/
++ETHERTYPESFILE:=$(ETHERTYPESPATH)/ethertypes
++
++BINPATH?=/sbin/
++BINFILE:=$(BINPATH)ebtables
+ 
+ PROGSPECS:=-DPROGVERSION=\"$(PROGVERSION)\" \
+ 	-DPROGNAME=\"$(PROGNAME)\" \
+@@ -57,10 +50,10 @@
+ 
+ .PHONY: exec
+ exec: ebtables
+-	install -m 0755 -o root -g root $< /sbin/ebtables
++	install -m 0755 -o root -g root $< $(BINFILE)
+ 
+ .PHONY: install
+-install: $(MANDIR)/man8/ebtables.8 ebtables $(ETHERTYPESFILE) exec
++install: $(MANDIR)/man8/ebtables.8 $(ETHERTYPESFILE) exec
+ 
+ .PHONY: clean
+ clean:
+--- ebtables-v2.0.2/ebtables.c	Tue Dec  3 22:52:12 2002
++++ ebtables-v2.0.3/ebtables.c	Tue Apr  1 20:08:15 2003
+@@ -52,6 +52,8 @@
+ #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+ #endif
+ #define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
++#define PRINT_VERSION printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n")
++
+ 
+ char *hooknames[NF_BR_NUMHOOKS] =
+ {
+@@ -201,8 +203,10 @@
+ 	strcpy(e->logical_out, "");
+ 	e->m_list = NULL;
+ 	e->w_list = NULL;
+-	// the init function of the standard target should have put the verdict
+-	// on CONTINUE
++	/*
++	 * the init function of the standard target should have put the verdict
++	 * on CONTINUE
++	 */
+ 	e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+ 	if (!e->t)
+ 		print_bug("Couldn't load standard target");
+@@ -293,7 +297,7 @@
+ 		merge[num_old + i].val += *options_offset;
+ 	}
+ 	memset(merge + num_old + num_new, 0, sizeof(struct option));
+-	// only free dynamically allocated stuff
++	/* only free dynamically allocated stuff */
+ 	if (oldopts != ebt_original_options)
+ 		free(oldopts);
+ 
+@@ -398,7 +402,7 @@
+ 	char *buf = NULL;
+ 	char *argv[3];
+ 
+-	// If they don't explicitly set it, read out of kernel
++	/* If they don't explicitly set it, read out of kernel */
+ 	if (!modprobe) {
+ 		buf = get_modprobe();
+ 		if (!buf)
+@@ -426,6 +430,37 @@
+ 	return 0;
+ }
+ 
++static void list_extensions()
++{
++	struct ebt_u_table *tbl = tables;
++        struct ebt_u_target *t = targets;
++        struct ebt_u_match *m = matches;
++        struct ebt_u_watcher *w = watchers;
++
++	PRINT_VERSION;
++	printf("Supported userspace extensions:\n\nSupported tables:\n");
++        while(tbl) {
++		printf("%s\n", tbl->name);
++                tbl = tbl->next;
++	}
++	printf("\nSupported targets:\n");
++        while(t) {
++		printf("%s\n", t->name);
++                t = t->next;
++	}
++	printf("\nSupported matches:\n");
++        while(m) {
++		printf("%s\n", m->name);
++                m = m->next;
++	}
++	printf("\nSupported watchers:\n");
++        while(w) {
++		printf("%s\n", w->name);
++                w = w->next;
++	}
++	exit(0);
++}
++
+ /*
+  * we use replace.flags, so we can't use the following values:
+  * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
+@@ -465,7 +500,7 @@
+ 	for (i = 0; i < entries->nentries; i++) {
+ 		if (replace.flags & LIST_N) {
+ 			digits = 0;
+-			// A little work to get nice rule numbers.
++			/* A little work to get nice rule numbers. */
+ 			j = i + 1;
+ 			while (j > 9) {
+ 				digits++;
+@@ -776,7 +811,8 @@
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+ 
+-	printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n"
++	PRINT_VERSION;
++	printf(
+ "Usage:\n"
+ "ebtables -[ADI] chain rule-specification [options]\n"
+ "ebtables -P chain target\n"
+@@ -799,7 +835,7 @@
+ "--atomic-commit               : update the kernel w/t table contained in <FILE>\n"
+ "--atomic-init                 : put the initial kernel table into <FILE>\n"
+ "--atomic-save                 : put the current kernel table into <FILE>\n"
+-"--atomic file                 : set <FILE> to file\n\n"
++"--atomic-file file            : set <FILE> to file\n\n"
+ "Options:\n"
+ "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+ "--src    -s [!] address[/mask]: source mac address\n"
+@@ -1140,7 +1176,7 @@
+ 	return -1;
+ }
+ 
+-// execute command A or I
++/* execute command A or I */
+ static void add_rule(int rule_nr)
+ {
+ 	int i, j;
+@@ -1301,7 +1337,7 @@
+ 	while(j--) {
+ 		u_e2 = *u_e;
+ 		*u_e = (*u_e)->next;
+-		// free everything
++		/* free everything */
+ 		free_u_entry(u_e2);
+ 		free(u_e2);
+ 	}
+@@ -1473,14 +1509,20 @@
+ 		e = entries->entries;
+ 		j = 0;
+ 		while (e) {
++			int chain_jmp;
++
+ 			j++;
+ 			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+ 				e = e->next;
+ 				continue;
+ 			}
+-			if (((struct ebt_standard_target *)e->t)->verdict == chain_nr)
++			chain_jmp = ((struct ebt_standard_target *)e->t)->verdict;
++			if (chain_jmp == chain_nr)
+ 				print_error("Can't delete the chain, it's referenced "
+ 				   "in chain %s, rule %d", entries->name, j);
++			/* adjust the chain jumps when necessary */
++			if (chain_jmp > chain_nr)
++				((struct ebt_standard_target *)e->t)->verdict--;
+ 			e = e->next;
+ 		}
+ 	}
+@@ -1698,7 +1740,9 @@
+ 				if (replace.selected_hook < NF_BR_NUMHOOKS)
+ 					print_error("You can't remove a standard chain");
+ 				/*
+-				 * if the chain is referenced, don't delete it
++				 * if the chain is referenced, don't delete it,
++				 * also decrement jumps to a chain behind the
++				 * one we're deleting
+ 				 */
+ 				check_for_references(replace.selected_hook - NF_BR_NUMHOOKS);
+ 				flush_chains();
+@@ -1791,7 +1835,7 @@
+ 			replace.command = 'V';
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+-			printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
++			PRINT_VERSION;
+ 			exit(0);
+ 
+ 		case 'M': /* modprobe */
+@@ -1811,6 +1855,10 @@
+ 				struct ebt_u_match *m;
+ 				struct ebt_u_watcher *w;
+ 
++				if (!strcasecmp("list_extensions",
++				   argv[optind]))
++					list_extensions();
++					
+ 				if ((m = find_match(argv[optind])))
+ 					add_match(m);
+ 				else if ((w = find_watcher(argv[optind])))
+@@ -2034,7 +2082,7 @@
+ 				            " or equal to 0x0600");
+ 			break;
+ 
+-		case 4  : // Lc
++		case 4  : /* Lc */
+ 			check_option(&replace.flags, LIST_C);
+ 			if (replace.command != 'L')
+ 				print_error("Use --Lc with -L");
+@@ -2042,7 +2090,7 @@
+ 				print_error("--Lx not compatible with --Lc");
+ 			replace.flags |= LIST_C;
+ 			break;
+-		case 5  : // Ln
++		case 5  : /* Ln */
+ 			check_option(&replace.flags, LIST_N);
+ 			if (replace.command != 'L')
+ 				print_error("Use --Ln with -L");
+@@ -2050,7 +2098,7 @@
+ 				print_error("--Lx not compatible with --Ln");
+ 			replace.flags |= LIST_N;
+ 			break;
+-		case 6  : // Lx
++		case 6  : /* Lx */
+ 			check_option(&replace.flags, LIST_X);
+ 			if (replace.command != 'L')
+ 				print_error("Use --Lx with -L");
+@@ -2060,7 +2108,7 @@
+ 				print_error("--Lx not compatible with --Ln");
+ 			replace.flags |= LIST_X;
+ 			break;
+-		case 8 : // atomic-commit
++		case 8 : /* atomic-commit */
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+--- ebtables-v2.0.2/communication.c	Sat Aug 24 23:01:36 2002
++++ ebtables-v2.0.3/communication.c	Tue Apr  1 20:08:23 2003
+@@ -5,11 +5,13 @@
+  *
+  */
+ 
+-// All the userspace/kernel communication is in this file.
+-// The other code should not have to know anything about the way the
+-// kernel likes the structure of the table data.
+-// The other code works with linked lists, lots of linked lists.
+-// So, the translation is done here.
++/*
++ * All the userspace/kernel communication is in this file.
++ * The other code should not have to know anything about the way the
++ * kernel likes the structure of the table data.
++ * The other code works with linked lists, lots of linked lists.
++ * So, the translation is done here.
++ */
+ 
+ #include <getopt.h>
+ #include <string.h>
+@@ -54,7 +56,7 @@
+ 	new->nentries = u_repl->nentries;
+ 	new->num_counters = u_repl->num_counters;
+ 	new->counters = u_repl->counters;
+-	// determine nr of udc
++	/* determine nr of udc */
+ 	i = 0;
+ 	cl = u_repl->udc;
+ 	while (cl) {
+@@ -63,7 +65,7 @@
+ 	}
+ 	i += NF_BR_NUMHOOKS;
+ 	chain_offsets = (unsigned int *)malloc(i * sizeof(unsigned int));
+-	// determine size
++	/* determine size */
+ 	i = 0;
+ 	cl = u_repl->udc;
+ 	while (1) {
+@@ -101,7 +103,7 @@
+ 			   sizeof(struct ebt_entry_target);
+ 			e = e->next;
+ 		}
+-		// a little sanity check
++		/* a little sanity check */
+ 		if (j != entries->nentries)
+ 			print_bug("Wrong nentries: %d != %d, hook = %s", j,
+ 			   entries->nentries, entries->name);
+@@ -115,7 +117,7 @@
+ 	if (!new->entries)
+ 		print_memory();
+ 
+-	// put everything in one block
++	/* put everything in one block */
+ 	p = new->entries;
+ 	i = 0;
+ 	cl = u_repl->udc;
+@@ -139,7 +141,7 @@
+ 		hlp->policy = entries->policy;
+ 		strcpy(hlp->name, entries->name);
+ 		hlp->counter_offset = entries->counter_offset;
+-		hlp->distinguisher = 0; // make the kernel see the light
++		hlp->distinguisher = 0; /* make the kernel see the light */
+ 		p += sizeof(struct ebt_entries);
+ 		e = entries->entries;
+ 		while (e) {
+@@ -184,7 +186,7 @@
+ 			if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+ 				struct ebt_standard_target *st =
+ 				   (struct ebt_standard_target *)p;
+-				// translate the jump to a udc
++				/* translate the jump to a udc */
+ 				if (st->verdict >= 0)
+ 					st->verdict = chain_offsets
+ 					   [st->verdict + NF_BR_NUMHOOKS];
+@@ -199,7 +201,7 @@
+ 		i++;
+ 	}
+ 
+-	// sanity check
++	/* sanity check */
+ 	if (p - new->entries != new->entries_size)
+ 		print_bug("Entries_size bug");
+ 	free(chain_offsets);
+@@ -212,7 +214,7 @@
+ 	int size;
+ 	FILE *file;
+ 
+-	// start from an empty file with right priviliges
++	/* start from an empty file with right priviliges */
+ 	command = (char *)malloc(strlen(filename) + 15);
+ 	if (!command)
+ 		print_memory();
+@@ -234,7 +236,7 @@
+ 	memcpy(data, repl, sizeof(struct ebt_replace));
+ 	memcpy(data + sizeof(struct ebt_replace), repl->entries,
+ 	   repl->entries_size);
+-	// initialize counters to zero, deliver_counters() can update them
++	/* initialize counters to zero, deliver_counters() can update them */
+ 	memset(data + sizeof(struct ebt_replace) + repl->entries_size,
+ 	   0, repl->nentries * sizeof(struct ebt_counter));
+ 	if (!(file = fopen(filename, "wb")))
+@@ -252,13 +254,13 @@
+ 	socklen_t optlen;
+ 	struct ebt_replace *repl;
+ 
+-	// translate the struct ebt_u_replace to a struct ebt_replace
++	/* translate the struct ebt_u_replace to a struct ebt_replace */
+ 	repl = translate_user2kernel(u_repl);
+ 	if (u_repl->filename != NULL) {
+ 		store_table_in_file(u_repl->filename, repl);
+ 		return;
+ 	}
+-	// give the data to the kernel
++	/* give the data to the kernel */
+ 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	get_sockfd();
+ 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+@@ -276,7 +278,10 @@
+ 
+ 	if (!(file = fopen(filename, "r+b")))
+ 		print_error("Could not open file %s", filename);
+-	// find out entries_size and then set the file pointer to the counters
++	/* 
++	 * find out entries_size and then set the file pointer to the
++	 * counters
++	 */
+ 	if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
+ 	   || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
+ 	   sizeof(unsigned int) ||
+@@ -291,9 +296,8 @@
+ 	fclose(file);
+ }
+ 
+-// gets executed after deliver_table
+-void
+-deliver_counters(struct ebt_u_replace *u_repl)
++/* gets executed after deliver_table */
++void deliver_counters(struct ebt_u_replace *u_repl)
+ {
+ 	unsigned short *point;
+ 	struct ebt_counter *old, *new, *newcounters;
+@@ -314,21 +318,23 @@
+ 	point = counterchanges;
+ 	while (*point != CNT_END) {
+ 		if (*point == CNT_NORM) {
+-			// 'normal' rule, meaning we didn't do anything to it
+-			// So, we just copy
++			/*
++			 *'normal' rule, meaning we didn't do anything to it
++			 * So, we just copy
++			 */
+ 			new->pcnt = old->pcnt;
+-			// we've used an old counter
++			/* we've used an old counter */
+ 			old++;
+-			// we've set a new counter
++			/* we've set a new counter */
+ 			new++;
+ 		} else if (*point == CNT_DEL) {
+-			// don't use this old counter
++			/* don't use this old counter */
+ 			old++;
+ 		} else if (*point == CNT_ADD) {
+-			// new counter, let it stay 0
++			/* new counter, let it stay 0 */
+ 			new++;
+ 		} else {
+-			// zero it (let it stay 0)
++			/* zero it (let it stay 0) */
+ 			old++;
+ 			new++;
+ 		}
+@@ -344,7 +350,7 @@
+ 	}
+ 	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
+ 	   sizeof(struct ebt_replace);
+-	// now put the stuff in the kernel's struct ebt_replace
++	/* now put the stuff in the kernel's struct ebt_replace */
+ 	repl.counters = u_repl->counters;
+ 	repl.num_counters = u_repl->num_counters;
+ 	memcpy(repl.name, u_repl->name, sizeof(repl.name));
+@@ -406,7 +412,7 @@
+    int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
+    unsigned int valid_hooks, char *base)
+ {
+-	// an entry
++	/* an entry */
+ 	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+ 		struct ebt_u_entry *new;
+ 		struct ebt_u_match_list **m_l;
+@@ -417,7 +423,10 @@
+ 		if (!new)
+ 			print_memory();
+ 		new->bitmask = e->bitmask;
+-		// plain userspace code doesn't know about EBT_ENTRY_OR_ENTRIES
++		/*
++		 * plain userspace code doesn't know about
++		 * EBT_ENTRY_OR_ENTRIES
++		 */
+ 		new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
+ 		new->invflags = e->invflags;
+ 		new->ethproto = e->ethproto;
+@@ -447,7 +456,7 @@
+ 			            "userspace tool", t->u.name);
+ 		memcpy(new->t, t, t->target_size +
+ 		   sizeof(struct ebt_entry_target));
+-		// deal with jumps to udc
++		/* deal with jumps to udc */
+ 		if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
+ 			char *tmp = base;
+ 			int verdict = ((struct ebt_standard_target *)t)->verdict;
+@@ -468,13 +477,13 @@
+ 			}
+ 		}
+ 
+-		// I love pointers
++		/* I love pointers */
+ 		**u_e = new;
+ 		*u_e = &new->next;
+ 		(*cnt)++;
+ 		(*totalcnt)++;
+ 		return 0;
+-	} else { // a new chain
++	} else { /* a new chain */
+ 		int i;
+ 		struct ebt_entries *entries = (struct ebt_entries *)e;
+ 		struct ebt_u_chain_list *cl;
+@@ -487,8 +496,8 @@
+ 			if (valid_hooks & (1 << i))
+ 				break;
+ 		*hook = i;
+-		// makes use of fact that standard chains come before udc
+-		if (i >= NF_BR_NUMHOOKS) { // udc
++		/* makes use of fact that standard chains come before udc */
++		if (i >= NF_BR_NUMHOOKS) { /* udc */
+ 			i -= NF_BR_NUMHOOKS;
+ 			cl = u_repl->udc;
+ 			while (i-- > 0)
+@@ -500,7 +509,7 @@
+ 	}
+ }
+ 
+-// initialize all chain headers
++/* initialize all chain headers */
+ static int
+ ebt_translate_chains(struct ebt_entry *e, unsigned int *hook,
+    struct ebt_u_replace *u_repl, unsigned int valid_hooks)
+@@ -514,10 +523,10 @@
+ 		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+ 			if (valid_hooks & (1 << i))
+ 				break;
+-		// makes use of fact that standard chains come before udc
+-		if (i >= NF_BR_NUMHOOKS) { // udc
++		/* makes use of fact that standard chains come before udc */
++		if (i >= NF_BR_NUMHOOKS) { /* udc */
+ 			chain_list = &u_repl->udc;
+-			// add in the back
++			/* add in the back */
+ 			while (*chain_list)
+ 				chain_list = &((*chain_list)->next);
+ 			*chain_list = (struct ebt_u_chain_list *)
+@@ -530,8 +539,10 @@
+ 			if (!((*chain_list)->udc))
+ 				print_memory();
+ 			new = (*chain_list)->udc;
+-			// ebt_translate_entry depends on this for knowing
+-			// to which chain is being jumped
++			/*
++			 * ebt_translate_entry depends on this for knowing
++			 * to which chain is being jumped
++			 */
+ 			(*chain_list)->kernel_start = (char *)e;
+ 		} else {
+ 			*hook = i;
+@@ -559,7 +570,9 @@
+ 
+ 	if (!(file = fopen(filename, "r+b")))
+ 		print_error("Could not open file %s", filename);
+-	// make sure table name is right if command isn't -L or --atomic-commit
++	/*
++	 * make sure table name is right if command isn't -L or --atomic-commit
++	 */
+ 	if (command != 'L' && command != 8) {
+ 		hlp = (char *)malloc(strlen(repl->name) + 1);
+ 		if (!hlp)
+@@ -596,7 +609,7 @@
+ 			print_memory();
+ 	} else
+ 		repl->counters = NULL;
+-	// copy entries and counters
++	/* copy entries and counters */
+ 	if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
+ 	   fread(repl->entries, sizeof(char), repl->entries_size, file)
+ 	   != repl->entries_size ||
+@@ -617,7 +630,7 @@
+ 
+ 	optlen = sizeof(struct ebt_replace);
+ 	get_sockfd();
+-	// --atomic-init || --init-table
++	/* --atomic-init || --init-table */
+ 	if (command == 7 || command == 11)
+ 		optname = EBT_SO_GET_INIT_INFO;
+ 	else
+@@ -635,7 +648,7 @@
+ 	else
+ 		repl->counters = NULL;
+ 
+-	// we want to receive the counters
++	/* we want to receive the counters */
+ 	repl->num_counters = repl->nentries;
+ 	optlen += repl->entries_size + repl->num_counters *
+ 	   sizeof(struct ebt_counter);
+@@ -658,12 +671,14 @@
+ 	strcpy(repl.name, u_repl->name);
+ 	if (u_repl->filename != NULL) {
+ 		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
+-		// -L with a wrong table name should be dealt with silently
++		/*
++		 * -L with a wrong table name should be dealt with silently
++		 */
+ 		strcpy(u_repl->name, repl.name);
+ 	} else if (retrieve_from_kernel(&repl, u_repl->command) == -1)
+ 		return -1;
+ 
+-	// translate the struct ebt_replace to a struct ebt_u_replace
++	/* translate the struct ebt_replace to a struct ebt_u_replace */
+ 	u_repl->valid_hooks = repl.valid_hooks;
+ 	u_repl->nentries = repl.nentries;
+ 	u_repl->num_counters = repl.num_counters;
+@@ -672,10 +687,13 @@
+ 	hook = -1;
+ 	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
+ 	   &hook, u_repl, u_repl->valid_hooks);
+-	i = 0; // holds the expected nr. of entries for the chain
+-	j = 0; // holds the up to now counted entries for the chain
+-	k = 0; // holds the total nr. of entries,
+-	       // should equal u_repl->nentries afterwards
++	i = 0; /* holds the expected nr. of entries for the chain */
++	j = 0; /* holds the up to now counted entries for the chain */
++	/*
++	 * holds the total nr. of entries,
++	 * should equal u_repl->nentries afterwards
++	 */
++	k = 0;
+ 	hook = -1;
+ 	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
+ 	   &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks, repl.entries);
+--- ebtables-v2.0.2/extensions/ebt_ip.c	Thu Oct 17 23:21:16 2002
++++ ebtables-v2.0.3/extensions/ebt_ip.c	Tue Apr  1 09:39:24 2003
+@@ -2,7 +2,7 @@
+  *  ebtables ebt_ip: IP extension module for userspace
+  * 
+  *  Authors:
+- *   Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *   Bart De Schuymer <bdschuym@pandora.be>
+  *
+  *  Changes:
+  *    added ip-sport and ip-dport; parsing of port arguments is
+@@ -36,7 +36,7 @@
+ 
+ #define IP_SOURCE '1'
+ #define IP_DEST   '2'
+-#define IP_myTOS  '3' // include/bits/in.h seems to already define IP_TOS
++#define IP_myTOS  '3' /* include/bits/in.h seems to already define IP_TOS */
+ #define IP_PROTO  '4'
+ #define IP_SPORT  '5'
+ #define IP_DPORT  '6'
+@@ -57,7 +57,7 @@
+ 	{ 0 }
+ };
+ 
+-// put the ip string into 4 bytes
++/* put the ip string into 4 bytes */
+ static int undot_ip(char *ip, unsigned char *ip2)
+ {
+ 	char *p, *q, *end;
+@@ -87,7 +87,7 @@
+ 	return 0;
+ }
+ 
+-// put the mask into 4 bytes
++/* put the mask into 4 bytes */
+ static int ip_mask(char *mask, unsigned char *mask2)
+ {
+ 	char *end;
+@@ -95,7 +95,7 @@
+ 	uint32_t mask22;
+ 
+ 	if (undot_ip(mask, mask2)) {
+-		// not the /a.b.c.e format, maybe the /x format
++		/* not the /a.b.c.e format, maybe the /x format */
+ 		bits = strtol(mask, &end, 10);
+ 		if (*end != '\0' || bits > 32 || bits < 0)
+ 			return -1;
+@@ -110,12 +110,12 @@
+ 	return 0;
+ }
+ 
+-// set the ip mask and ip address
++/* set the ip mask and ip address */
+ void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+ {
+ 	char *p;
+ 
+-	// first the mask
++	/* first the mask */
+ 	if ((p = strrchr(address, '/')) != NULL) {
+ 		*p = '\0';
+ 		if (ip_mask(p + 1, (unsigned char *)msk))
+@@ -129,7 +129,7 @@
+ 	*addr = *addr & *msk;
+ }
+ 
+-// transform the ip mask into a string ready for output
++/* transform the ip mask into a string ready for output */
+ char *mask_to_dotted(uint32_t mask)
+ {
+ 	int i;
+@@ -138,14 +138,14 @@
+ 
+ 	maskaddr = ntohl(mask);
+ 
+-	// don't print /32
++	/* don't print /32 */
+ 	if (mask == 0xFFFFFFFFL) {
+ 		*buf = '\0';
+ 		return buf;
+ 	}
+ 
+ 	i = 32;
+-	bits = 0xFFFFFFFEL; // case 0xFFFFFFFF has just been dealt with
++	bits = 0xFFFFFFFEL; /* case 0xFFFFFFFF has just been dealt with */
+ 	while (--i >= 0 && maskaddr != bits)
+ 		bits <<= 1;
+ 
+@@ -154,7 +154,7 @@
+ 	else if (!i)
+ 		*buf = '\0';
+ 	else
+-		// mask was not a decent combination of 1's and 0's
++		/* mask was not a decent combination of 1's and 0's */
+ 		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
+ 		   ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
+ 		   ((unsigned char *)&mask)[3]);
+@@ -162,7 +162,7 @@
+ 	return buf;
+ }
+ 
+-// transform a protocol and service name into a port number
++/* transform a protocol and service name into a port number */
+ static uint16_t parse_port(const char *protocol, const char *name)
+ {
+ 	struct servent *service;
+@@ -313,10 +313,19 @@
+ 			ipinfo->invflags |= EBT_IP_PROTO;
+ 		if (optind > argc)
+ 			print_error("Missing IP protocol argument");
+-		i = strtol(argv[optind - 1], &end, 10);
+-		if (i < 0 || i > 255 || *end != '\0')
+-			print_error("Problem with specified IP protocol");
+-		ipinfo->protocol = i;
++		(unsigned char) i = strtoul(argv[optind - 1], &end, 10);
++		if (*end != '\0') {
++			struct protoent *pe;
++
++			pe = getprotobyname(argv[optind - 1]);
++			if (pe == NULL)
++				print_error
++				    ("Unknown specified IP protocol - %s",
++				     argv[optind - 1]);
++			ipinfo->protocol = pe->p_proto;
++		} else {
++			ipinfo->protocol = (unsigned char) i;
++		}
+ 		ipinfo->bitmask |= EBT_IP_PROTO;
+ 		break;
+ 	default:
+@@ -335,13 +344,13 @@
+ 		print_error("For IP filtering the protocol must be "
+ 		            "specified as IPv4");
+ 
+- 	if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
+- 		(!ipinfo->bitmask & EBT_IP_PROTO || 
+- 		ipinfo->invflags & EBT_IP_PROTO ||
+- 		(ipinfo->protocol!=IPPROTO_TCP && 
+- 			ipinfo->protocol!=IPPROTO_UDP)))
+- 		print_error("For port filtering the IP protocol must be "
+- 		            "either 6 (tcp) or 17 (udp)");
++	if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
++		(!(ipinfo->bitmask & EBT_IP_PROTO) || 
++		ipinfo->invflags & EBT_IP_PROTO ||
++		(ipinfo->protocol!=IPPROTO_TCP && 
++			ipinfo->protocol!=IPPROTO_UDP)))
++		print_error("For port filtering the IP protocol must be "
++		            "either 6 (tcp) or 17 (udp)");
+ }
+ 
+ static void print(const struct ebt_u_entry *entry,
+@@ -375,10 +384,17 @@
+ 		printf("0x%02X ", ipinfo->tos);
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_PROTO) {
++		struct protoent *pe;
++
+ 		printf("--ip-proto ");
+ 		if (ipinfo->invflags & EBT_IP_PROTO)
+ 			printf("! ");
+-		printf("%d ", ipinfo->protocol);
++		pe = getprotobynumber(ipinfo->protocol);
++		if (pe == NULL) {
++			printf("%d ", ipinfo->protocol);
++		} else {
++			printf("%s ", pe->p_name);
++		}
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP_SPORT) {
+ 		printf("--ip-sport ");
+@@ -427,11 +443,13 @@
+ 			return 0;
+ 	}
+ 	if (ipinfo1->bitmask & EBT_IP_SPORT) {
+-		if (ipinfo1->sport != ipinfo2->sport)
++		if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
++		   ipinfo1->sport[1] != ipinfo2->sport[1])
+ 			return 0;
+ 	}
+ 	if (ipinfo1->bitmask & EBT_IP_DPORT) {
+-		if (ipinfo1->dport != ipinfo2->dport)
++		if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
++		   ipinfo1->dport[1] != ipinfo2->dport[1])
+ 			return 0;
+ 	}
+ 	return 1;
+--- ebtables-v2.0.2/extensions/ebt_arp.c	Fri Nov 22 20:43:47 2002
++++ ebtables-v2.0.3/extensions/ebt_arp.c	Fri Jan 10 00:20:17 2003
+@@ -23,7 +23,7 @@
+ };
+ 
+ #define NUMOPCODES 9
+-// a few names
++/* a few names */
+ static char *opcodes[] =
+ {
+ 	"Request",
+@@ -64,7 +64,7 @@
+ 	arpinfo->bitmask = 0;
+ }
+ 
+-// defined in ebt_ip.c
++/* defined in ebt_ip.c */
+ void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+ 
+ #define OPT_OPCODE 0x01
+@@ -188,7 +188,7 @@
+ 		            "specified as ARP or RARP");
+ }
+ 
+-// defined in the ebt_ip.c
++/* defined in the ebt_ip.c */
+ char *mask_to_dotted(uint32_t mask);
+ 
+ static void print(const struct ebt_u_entry *entry,
+--- ebtables-v2.0.2/extensions/ebt_log.c	Sat Aug 24 15:26:34 2002
++++ ebtables-v2.0.3/extensions/ebt_log.c	Fri Jan 10 00:21:41 2003
+@@ -5,16 +5,18 @@
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_log.h>
+ 
+-// copied from syslog.h
+-// used for the LOG target
+-#define	LOG_EMERG	0 // system is unusable
+-#define	LOG_ALERT	1 // action must be taken immediately
+-#define	LOG_CRIT	2 // critical conditions
+-#define	LOG_ERR		3 // error conditions
+-#define	LOG_WARNING	4 // warning conditions
+-#define	LOG_NOTICE	5 // normal but significant condition
+-#define	LOG_INFO	6 // informational
+-#define	LOG_DEBUG	7 // debug-level messages
++/*
++ * copied from syslog.h
++ * used for the LOG target
++ */
++#define	LOG_EMERG	0 /* system is unusable               */
++#define	LOG_ALERT	1 /* action must be taken immediately */
++#define	LOG_CRIT	2 /* critical conditions              */
++#define	LOG_ERR		3 /* error conditions                 */
++#define	LOG_WARNING	4 /* warning conditions               */
++#define	LOG_NOTICE	5 /* normal but significant condition */
++#define	LOG_INFO	6 /* informational                    */
++#define	LOG_DEBUG	7 /* debug-level messages             */
+ 
+ #define LOG_DEFAULT_LEVEL LOG_INFO
+ 
+@@ -41,7 +43,7 @@
+ 	for (i = 0; i < 8; i++)
+ 		if (!strcmp(arg, eight_priority[i].c_name))
+ 			return eight_priority[i].c_val;
+-	// return bad loglevel
++	/* return bad loglevel */
+ 	return 9;
+ }
+ 
+--- ebtables-v2.0.2/extensions/ebt_mark_m.c	Sat Aug 24 15:26:34 2002
++++ ebtables-v2.0.3/extensions/ebt_mark_m.c	Fri Jan 10 00:22:15 2003
+@@ -9,7 +9,7 @@
+ 
+ static struct option opts[] =
+ {
+-	{ "mark"     , required_argument, 0, MARK },
++	{ "mark", required_argument, 0, MARK },
+ 	{ 0 }
+ };
+ 
+--- ebtables-v2.0.2/THANKS	Sat Jun  1 21:24:51 2002
++++ ebtables-v2.0.3/THANKS	Tue Apr  1 20:15:15 2003
+@@ -1,4 +1,4 @@
+-Thanks go out to:
++Special thanks go out to these early contributors:
+ 
+ Lennert Buytenhek
+ Rusty Russel
+--- ebtables-v2.0.2/ChangeLog	Tue Dec  3 23:00:45 2002
++++ ebtables-v2.0.3/ChangeLog	Tue Apr  1 20:07:24 2003
+@@ -1,3 +1,10 @@
++20030402
++	* fixed check bug in ebt_ip.c (report from
++	  joe_judge_at_guardium.com).
++20030111
++	* fixed problem when removing a chain (report from
++	  ykphuah_at_greenpacket.com).
++	* Added --help list_extensions which, well, lists the extensions
+ 20021203
+ 	* changed the way to use the atomic operations. It's now possible
+ 	  to use the EBTABLES_ATOMIC_FILE environment variable, so it's no
+--- ebtables-v2.0.2/ebtables.8	Sat Dec  7 13:42:58 2002
++++ ebtables-v2.0.3/ebtables.8	Tue Apr  1 20:15:04 2003
+@@ -1,8 +1,11 @@
+-.TH EBTABLES 8  "03 December 2002"
++.TH EBTABLES 8  "15 March 2003"
+ .\"
+ .\" Man page written by Bart De Schuymer <bdschuym@pandora.be>
+ .\" It is based on the iptables man page.
+ .\"
++.\" The man page was edited by 
++.\"      Greg Morgan <" dr_kludge_at_users_sourceforge_net >
++.\"
+ .\" Iptables page by Herve Eychenne March 2000.
+ .\"
+ .\"     This program is free software; you can redistribute it and/or modify
+@@ -23,46 +26,61 @@
+ .SH NAME
+ ebtables (v.2.0) \- Ethernet bridge frame table administration
+ .SH SYNOPSIS
+-.BR "ebtables -[ADI] " "chain rule-specification " [ options ]
++.BR "ebtables [-t table] -[ADI] " "chain rule-specification [match-extensions] [watcher-extensions] TARGET"
++.br
++.BR "ebtables [-t table] -P " "chain " "ACCEPT | DROP | RETURN"
+ .br
+-.BR "ebtables -P " "chain target"
++.BR "ebtables [-t table] -F [" "chain" "]"
+ .br
+-.BR "ebtables -[FLZ] [" "chain" "]"
++.BR "ebtables [-t table] -Z [" "chain" "]"
+ .br
+-.BR "ebtables -[NX] " chain
++.BR "ebtables [-t table] -L [-Z] [" chain "] [ [" --Ln "] [" --Lc "] ] " | " [" --Lx "]"
+ .br
+-.BR "ebtables -E " "old-chain-name new-chain-name"
++.BR "ebtables [-t table] -[NX] " chain
+ .br
+-.BR "ebtables --init-table"
++.BR "ebtables [-t table] -E " "old-chain-name new-chain-name"
+ .br
+-.BR "ebtables --atomic-init "
++.BR "ebtables [-t table] --init-table"
+ .br
+-.BR "ebtables --atomic-save "
++.BR "ebtables [-t table] [--atomic-file file] --atomic-commit
+ .br
+-.BR "ebtables --atomic-commit "
++.BR "ebtables [-t table] [--atomic-file file] --atomic-init"
++.br
++.BR "ebtables [-t table] [--atomic-file file] --atomic-save"
+ .br
+ .SH DESCRIPTION
+ .B ebtables
+-is used to set up, maintain, and inspect the tables of Ethernet frame
+-rules in the Linux kernel. It works analogous as iptables, but is less
+-complicated. This man page is written with the man page of iptables
+-next to it, so don't be surprised to see copied sentences and structure.
++is a user space tool, it is used to set up and maintain the
++tables of Ethernet frame rules in the Linux kernel. These rules inspect
++the Ethernet frames which they see.
++.B ebtables
++is analogous to the
++.B iptables
++user space tool, but
++.B ebtables
++is less complicated.
+ 
+-There are three tables with built-in chains. Each chain is a list
+-of rules which can match frames: each rule specifies what to do with a
+-frame which matches. This is called a 'target'. The tables are used to
+-divide functionality into different sets of chains.
++.SS CHAINS
++There are three Ethernet frame tables with built-in chains in the
++Linux kernel. The kernel tables are used to divide functionality into
++different sets of rules. Each set of rules is called a chain.
++Each chain is an ordered list of rules that can match Ethernet frames. If a
++rule matches an Ethernet frame, then a processing specification tells
++what to do with that matching frame. The processing specification is
++called a 'target'. However, if the frame does not match the current
++rule in the chain, then the next rule in the chain is examined and so forth.
++The user can create new (user-defined) chains which can be used as the 'target' of a rule.
+ 
+ .SS TARGETS
+-A firewall rule specifies criteria for a frame, and a target. If the
+-frame does not match, the next rule in the chain is the examined one; if
+-it does match, then the next thing to do is specified by the target.
+-This target can be one of these values:
++A firewall rule specifies criteria for an Ethernet frame and a frame
++processing specification called a target.  When a frame matches a rule,
++then the next action performed by the kernel is specified by the target.
++The target can be one of these values:
+ .IR ACCEPT ,
+ .IR DROP ,
+ .IR CONTINUE ,
+ .IR RETURN ,
+-an extention.
++an 'extension' (see below) or a user-defined chain.
+ .PP
+ .I ACCEPT
+ means to let the frame through.
+@@ -74,26 +92,36 @@
+ .I RETURN
+ means stop traversing this chain and resume at the next rule in the
+ previous (calling) chain.
+-For the
+-other targets see the
++For the extension targets please see the
+ .B "TARGET EXTENSIONS"
+-section.
++section of this man page.
+ .SS TABLES
+-There are three tables.
++As stated earlier, there are three Ethernet frame tables in the Linux
++kernel.  The tables are
++.BR filter ", " nat " and " broute .
++Of these three tables,
++the filter table is the default table that the
++.B ebtables
++command operates on.
++If you are working with the filter table, then you can drop the '-t filter'
++argument to the ebtables command.  However, you will need to provide
++the -t argument for the other two tables.  The -t argument must be the
++first argument on the ebtables command line, if used. 
+ .TP
+ .B "-t, --table"
+-This option specifies the frame matching table which the command should
+-operate on. If specified it should be the first option. The tables are: 
++.br
+ .BR filter ,
+-this is the default table and contains three chains: 
++is the default table and contains three built-in chains:
+ .B INPUT 
+ (for frames destined for the bridge itself), 
+ .B OUTPUT 
+ (for locally-generated frames) and
+ .B FORWARD 
+ (for frames being bridged).
++.br
++.br
+ .BR nat ,
+-this table is used to change the mac addresses and contains three chains: 
++is used to change the mac addresses and contains three built-in chains:
+ .B PREROUTING 
+ (for altering frames as soon as they come in), 
+ .B OUTPUT 
+@@ -104,30 +132,38 @@
+ PREFORWARDING and POSTFORWARDING, but for all those who come from the
+ .BR iptables " world to " ebtables
+ it is easier to have the same names.
++.br
++.br
+ .BR broute ,
+-this table is used to make a brouter, it has one chain:
++is used to make a brouter, it has one built-in chain:
+ .BR BROUTING .
+ The targets
+ .BR DROP " and " ACCEPT
+-have special meaning in this table.
++have special meaning in the broute table.
+ .B DROP
+ actually means the frame has to be routed, while
+ .B ACCEPT
+ means the frame has to be bridged. The
+ .B BROUTING
+ chain is traversed very early. It is only traversed by frames entering on
+-a bridge enslaved nic that is in forwarding state. Normally those frames
++a bridge enslaved NIC that is in forwarding state. Normally those frames
+ would be bridged, but you can decide otherwise here. The
+ .B redirect
+ target is very handy here.
+-.SH OPTIONS
+-The options can be divided into several different groups.
++.SH EBTABLES COMMAND LINE ARGUMENTS
++After the initial ebtables -t, table command line argument, the remaining
++arguments can be divided into several different groups.  These groups
++are commands, miscellaneous commands, rule-specifications, match-extensions,
++and watcher-extensions.
+ .SS COMMANDS
+-These options specify the specific actions to perform; only one of them
+-can be specified on the command line (the
+-.B -Z
+-command is an exception). All these options only apply to the selected
+-(or default) table.
++The ebtables command arguments specify the actions to perform on the table
++defined with the -t argument.  If you do not use the -t argument to name
++a table, the commands apply to the default filter table.
++With the exception of both the
++.B "-Z"
++and
++.B "--atomic-file"
++commands, only one command may be used on the command line at a time.
+ .TP
+ .B "-A, --append"
+ Append a rule to the end of the selected chain.
+@@ -139,71 +175,119 @@
+ the complete rule as it would have been specified when it was added.
+ .TP
+ .B "-I, --insert"
+-Insert the specified rule into the selected chain at the specified rule number (1 meaning
+-the head of the chain).
++Insert the specified rule into the selected chain at the specified rule number.
++The number one, 1, means the head of the chain.
++.TP
++.B "-P, --policy"
++Set the policy for the chain to the given target. The policy can be
++.BR ACCEPT ", " DROP " or " RETURN .
++.TP
++.B "-F, --flush"
++Flush the selected chain. If no chain is selected, then every chain will be
++flushed. Flushing the chain does not change the policy of the
++chain, however.
++.TP
++.B "-Z, --zero"
++Set the counters of the selected chain to zero. If no chain is selected, all the counters
++are set to zero. The
++.B "-Z"
++command can be used in conjunction with the 
++.B "-L"
++command.
++When both the
++.B "-Z"
++and
++.B "-L"
++commands are used together in this way, the rule counters are printed on the screen
++before they are set to zero.
+ .TP
+ .B "-L, --list"
+ List all rules in the selected chain. If no chain is selected, all chains
+ are listed.
+ .br
+-The following three options change the output:
++The following three options change the output of the
++.B "-L"
++list command:
+ .br
+ .B "--Ln"
+ .br
+-Puts rule numbers in front of every rule.
++Places the rule number in front of every rule.
+ .br
+ .B "--Lc"
+ .br
+-Shows the counters at the end of every rule, there is a frame counter
+-(pcnt) and a byte counter (bcnt).
++Shows the counters at the end of each rule displayed by the
++.B "-L"
++command. Both a frame counter (pcnt) and a byte counter (bcnt) are displayed.
+ .br
+ .B "--Lx"
+ .br
+-The output is directly usable as executable commands in a script, to be
+-run f.e. at bootup. This option is incompatible with the previous two
+-options. When no chain name was specified for the
++The output of the 
++.B "--Lx"
++option may be used to create a set of
++.B ebtables
++commands.  You may use this set of commands in an
++.B ebtables
++boot or reload
++script.  For example the output could be used at system startup.
++The 
++.B "--Lx"
++option is incompatible with both of the other
++.B "--Ln"
++and
++.B "--Lc"
++chain listing options, 
++.B "-L."
++All necessary
++.B ebtables
++commands for making the current list of
++user-defined chains in the kernel and any commands issued by the user to
++rename the standard
++.B ebtables
++chains will be listed, when no chain name is
++supplied for the
+ .B "-L"
+-command, all necessary commands for making the user defined chains and
+-renaming the standard chains will be made.
+-.TP
+-.B "-F, --flush"
+-Flush the selected chain. If no chain is selected, every chain will be
+-flushed. This does not change the policy of the chain.
+-.TP
+-.B "--init-table"
+-Replace the current table data by the initial table data.
+-.TP
+-.B "-Z, --zero"
+-Put the counters of the selected chain on zero. If no chain is selected, all the counters
+-are put on zero. This can be used in conjunction with the -L command (see above). 
+-This will cause the rule counters to be printed on the screen before they are put on zero.
+-.TP
+-.B "-P, --policy"
+-Set the policy for the chain to the given target. The policy can be
+-.BR ACCEPT ", " DROP " or " RETURN .
++command while using the
++.B "-Lx"
++option.
+ .TP
+ .B "-N, --new-chain"
+-Create a new user-defined chain by the given name. The number of
+-user-defined chains is unlimited. A chain name has max length of 31.
++Create a new user-defined chain with the given name. The number of
++user-defined chains is unlimited. A user-defined chain name has maximum
++length of 31 characters.
+ .TP
+ .B "-X, --delete-chain"
+-Delete the specified user-defined chain. There must be no references to the
+-chain,
++Delete the specified user-defined chain. There must be no remaining references
++to the to be deleted chain.  Otherwise,
+ .B ebtables
+ will complain if there are.
+ .TP
+ .B "-E, --rename-chain"
+-Rename the specified chain to the new name. This has no effect on the
+-structure of the table. It is also allowed to rename a base chain, f.e.
+-if you like PREBRIDGING more than PREROUTING. Be sure to talk about the
+-standard chain names when you would ask a question on a mailing list.
++Rename the specified chain to a new name.  Besides renaming a user-defined
++chain, you may rename a standard chain name to a name that suits your
++taste. For example, if you like PREBRIDGING more than PREROUTING,
++then you can use the -E command to rename the PREROUTING chain. If you do
++rename one of the standard
++.B ebtables
++chain names, please be sure to mention
++this fact should you post a question on the
++.B ebtables
++mailing lists.
++It would be wise to use the standard name in your post. Renaming a standard
++.B ebtables
++chain in this fashion has no effect on the structure or function
++of the
++.B ebtables
++kernel table.
++.TP
++.B "--init-table"
++Replace the current table data by the initial table data.
+ .TP
+ .B "--atomic-init"
+ Copy the kernel's initial data of the table to the specified
+ file. This can be used as the first action, after which rules are added
+ to the file. The file can be specified using the
+ .B --atomic-file
+-option or through the
++command or through the
+ .IR EBTABLES_ATOMIC_FILE " environment variable."
+ .TP
+ .B "--atomic-save"
+@@ -211,30 +295,81 @@
+ file. This can be used as the first action, after which rules are added
+ to the file. The file can be specified using the
+ .B --atomic-file
+-option or through the
++command or through the
+ .IR EBTABLES_ATOMIC_FILE " environment variable."
+ .TP
+ .B "--atomic-commit"
+ Replace the kernel table data with the data contained in the specified
+-file. This is a useful command that allows you to put all your rules of a
++file. This is a useful command that allows you to load all your rules of a
+ certain table into the kernel at once, saving the kernel a lot of precious
+ time and allowing atomic updates of the tables. The file which contains
+ the table data is constructed by using either the
+ .B "--atomic-init"
+ or the
+ .B "--atomic-save"
+-command to get a starting file. After that, using the
++command to generate a starting file. After that, using the
+ .B "--atomic-file"
+-option when constructing rules or setting the
++command when constructing rules or setting the
+ .IR EBTABLES_ATOMIC_FILE " environment variable"
+ allows you to extend the file and build the complete table before
+-commiting it to the kernel.
++committing it to the kernel.
++.TP
++.B "--atomic-file -Z"
++The counters stored in a file with, say,
++.B "--atomic-init"
++can be optionally zeroed by supplying the
++.B "-Z"
++command. You may also zero the counters by setting the
++.IR EBTABLES_ATOMIC_FILE " environment variable."
++
++.SS MISCELLANOUS COMMANDS
++.TP
++.B "-V, --version"
++Show the version of the ebtables userspace program.
++.TP
++.B "-h, --help"
++Give a brief description of the command syntax. Here you can also specify
++names of extensions and
++.B ebtables
++will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
++Specify
++.I list_extensions
++to list all extensions supported by the userspace
++utility.
++.TP
++.BR "-j, --jump " "\fItarget\fP"
++The target of the rule. This is one of the following values:
++.BR ACCEPT ,
++.BR DROP ,
++.BR CONTINUE ,
++.BR RETURN ,
++a target extension (see
++.BR "TARGET EXTENSIONS" ")"
++or a user-defined chain name.
++.TP
++.B --atomic-file file
++Let the command operate on the specified file. The data of the table to
++operate on will be extracted from the file and the result of the operation
++will be saved back into the file. If specified, this option should come
++before the command specification. An alternative that should be preferred,
++is setting the
++.IR EBTABLES_ATOMIC_FILE " environment variable."
++.TP
++.B -M, --modprobe program
++When talking to the kernel, use this program to try to automatically load
++missing kernel modules.
++
+ .SS
+-PARAMETERS
+-The following parameters make up a rule specification (as used in the add
+-and delete commands). A "!" argument before the specification inverts the
+-test for that specification. Apart from these standard parameters, there are others, see
+-.BR "MATCH EXTENSIONS" .
++RULE-SPECIFICATIONS
++The following command line arguments make up a rule specification (as used 
++in the add and delete commands). A "!" option before the specification 
++inverts the test for that specification. Apart from these standard rule 
++specifications there are some other command line arguments of interest.
++See both the 
++.BR "MATCH-EXTENSIONS" 
++and the
++.BR "WATCHER-EXTENSION(S)" 
++below.
+ .TP
+ .BR "-p, --protocol " "[!] \fIprotocol\fP"
+ The protocol that was responsible for creating the frame. This can be a
+@@ -305,7 +440,7 @@
+ .TP
+ .BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+ The source mac address. Both mask and address are written as 6 hexadecimal
+-numbers seperated by colons. Alternatively one can specify Unicast,
++numbers separated by colons. Alternatively one can specify Unicast,
+ Multicast or Broadcast.
+ .br
+ Unicast=00:00:00:00:00:00/01:00:00:00:00:00,
+@@ -320,45 +455,36 @@
+ .B --dst
+ is an alias for this option.
+ 
+-.SS OTHER OPTIONS
++.SS MATCH-EXTENSIONS
++.B ebtables
++extensions are precompiled into the userspace tool. So there is no need
++to explicitly load them with a -m option like in
++.BR iptables .
++However, these
++extensions deal with functionality supported by supplemental kernel modules.
++.SS arp
++Specify arp fields. These will only work if the protocol equals
++.BR ARP " or " RARP .
+ .TP
+-.B "-V, --version"
+-Show the version of the userprogram.
++.BR "--arp-opcode " "[!] \fIopcode\fP"
++The (r)arp opcode (decimal or a string, for more details see
++.BR "ebtables -h arp" ).
+ .TP
+-.B "-h, --help"
+-Give a brief description of the command syntax. Here you can also specify
+-names of extensions and
+-.B ebtables
+-will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
++.BR "--arp-htype " "[!] \fIhardware type\fP"
++The hardware type, this can be a decimal or the string "Ethernet". This
++is normally Ethernet (value 1).
+ .TP
+-.BR "-j, --jump " "\fItarget\fP"
+-The target of the rule. This is one of the following values:
+-.BR ACCEPT ,
+-.BR DROP ,
+-.BR CONTINUE ,
+-.BR RETURN ,
+-a target extension (see
+-.BR "TARGET EXTENSIONS" ")"
+-or a user defined chain name.
++.BR "--arp-ptype " "[!] \fIprotocol type\fP"
++The protocol type for which the (r)arp is used (hexadecimal or the string "IPv4").
++This is normally IPv4 (0x0800).
+ .TP
+-.B --atomic-file file
+-Let the command operate on the specified file. The data of the table to
+-operate on will be extracted from the file and the result of the operation
+-will be saved back into the file. If specified, this option should come
+-before the command specification. An alternative that should be preferred,
+-is setting the
+-.BR EBTABLES_ATOMIC_FILE "environment variable."
++.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP IP source address specification.
+ .TP
+-.B -M, --modprobe program
+-When talking to the kernel, use this program to try to automatically load
+-missing kernel modules.
+-.SH MATCH EXTENSIONS
+-.B ebtables
+-extensions are precompiled into the userspace tool. So there is no need
+-to explicitly load them with a -m option like in iptables. However, these
+-extensions deal with functionality supported by supplemental kernel modules.
++.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP IP destination address specification.
+ .SS ip
+-Specify ip specific fields. These will only work if the protocol equals
++Specify ip fields. These will only work if the protocol equals
+ .BR IPv4 .
+ .TP
+ .BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]"
+@@ -395,59 +521,38 @@
+ 17 (UDP). The flag
+ .B --ip-dport
+ is an alias for this option.
+-.SS arp
+-Specify arp specific fields. These will only work if the protocol equals
+-.BR ARP " or " RARP .
+-.TP
+-.BR "--arp-opcode " "[!] \fIopcode\fP"
+-The (r)arp opcode (decimal or a string, for more details see
+-.BR "ebtables -h arp" ).
+-.TP
+-.BR "--arp-htype " "[!] \fIhardware type\fP"
+-The hardware type, this can be a decimal or the string "Ethernet". This
+-is normally Ethernet (value 1).
+-.TP
+-.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+-The protocol type for which the (r)arp is used (hexadecimal or the string "IPv4").
+-This is normally IPv4 (0x0800). 
+-.TP
+-.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+-The ARP IP source address specification.
++.SS mark_m
+ .TP
+-.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+-The ARP IP destination address specification.
++.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
++Matches frames with the given unsigned mark value. If a mark value and
++mask is specified, the logical AND of the mark value of the frame and
++the user-specified mask is taken before comparing it with the user-specified
++mark value. If only a mask is specified (start with '/') the logical AND
++of the mark value of the frame and the user-specified mark is taken and
++the result is compared with zero.
+ .SS vlan
+-Specify 802.1Q Tag Control Information fields. These will only work if the protocol equals
+-.BR 802_1Q.
+-Also see extension help by 
+-.BR "ebtables -h vlan" .
++Specify 802.1Q Tag Control Information fields.
++The protocol rule specification (frame type) should be set to
++.BR 802_1Q " (0x8100)."
+ .TP
+ .BR "--vlan-id " "[!] \fIid\fP"
+-The VLAN identifier field, VID (decimal number from 0 to 4094).
++The VLAN identifier field (VID). Decimal number from 0 to 4095.
+ .TP
+ .BR "--vlan-prio " "[!] \fIprio\fP"
+-The user_priority field, this can be a decimal number from 0 to 7. 
+-Required VID to be 0 (null VID) or not specified vlan-id parameter (in this case VID deliberately be set to 0).
++The user_priority field. Decimal number from 0 to 7.
++The VID should be set to 0 ("null VID") or unspecified
++(for this case the VID is deliberately set to 0).
+ .TP
+ .BR "--vlan-encap " "[!] \fItype\fP"
+-The encapsulated Ethernet frame type/length, this can be a hexadecimal
+-number from 0x0000 to 0xFFFF.
+-Usually it's 0x0800 (IPv4). See also 
+-.B /etc/ethertypes 
+-file.
+-.SS mark_m
+-.TP
+-.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
+-Matches frames with the given unsigned mark value. If a mark value and 
+-mask is specified, the logical AND of the mark value of the frame and
+-the user specified mask is taken before comparing with the user specified
+-mark value. If only a mask is specified (start with '/') the logical AND
+-of the mark value of the frame and the user specified mark is taken and
+-the result is compared with zero.
++The encapsulated Ethernet frame type/length.
++Specified as hexadecimal
++number from 0x0000 to 0xFFFF or as a symbolic name
++from
++.BR /etc/ethertypes .
+ 
+-.SH WATCHER EXTENSION(S)
++.SS WATCHER-EXTENSION(S)
+ Watchers are things that only look at frames passing by. These watchers only
+-see the frame if the frame passes all the matches of the rule.
++see the frame if the frame matches the rule.
+ .SS log
+ The fact that the log module is a watcher lets us log stuff while giving a target
+ by choice. Note that the log module therefore is not a target.
+@@ -477,31 +582,7 @@
+ will log the (r)arp information when a frame made by the (r)arp protocols
+ matches the rule. The default is no (r)arp information logging.
+ .SS TARGET EXTENSIONS
+-.TP
+-.B snat
+-The
+-.B snat
+-target can only be used in the
+-.BR POSTROUTING " chain of the " nat " table."
+-It specifies that the source mac address has to be changed.
+-.br
+-.BR "--to-source " "\fIaddress\fP"
+-.br
+-The flag
+-.B --to-src
+-is an alias for this option.
+-.br
+-.BR "--snat-target " "\fItarget\fP"
+-.br
+-Specifies the standard target. After doing the snat, the rule still has 
+-to give a standard target so
+-.B ebtables
+-knows what to do.
+-The default target is ACCEPT. Making it CONTINUE could let you use
+-multiple target extensions on the same frame. Making it DROP doesn't
+-make sense, but you could do that too. RETURN is also allowed. Note
+-that using RETURN in a base chain is not allowed.
+-.TP
++.SS
+ .B dnat
+ The
+ .B dnat
+@@ -509,25 +590,45 @@
+ .BR BROUTING " chain of the " broute " table and the "
+ .BR PREROUTING " and " OUTPUT " chains of the " nat " table."
+ It specifies that the destination mac address has to be changed.
+-.br
++.TP
+ .BR "--to-destination " "\fIaddress\fP"
+ .br
+ The flag
+ .B --to-dst
+ is an alias for this option.
+-.br
++.TP
+ .BR "--dnat-target " "\fItarget\fP"
+ .br
+ Specifies the standard target. After doing the dnat, the rule still has to
+ give a standard target so
+ .B ebtables
+ knows what to do.
+-The default target is ACCEPT. Making it CONTINUE could let you use 
++The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP only makes
+ sense in the BROUTING chain but using the redirect target is more logical
+ there. RETURN is also allowed. Note
+ that using RETURN in a base chain is not allowed.
++.SS
++.B mark
++The mark target can be used in every chain of every table. It is possible
++to use the marking of a frame/packet in both ebtables and iptables,
++if the br-nf code is compiled into the kernel. Both put the marking at the
++same place. So, you can consider this fact as a feature, or as something to
++watch out for.
++.TP
++.BR "--set-mark " "\fIvalue\fP"
++.br
++Mark the frame with the specified unsigned value.
+ .TP
++.BR "--mark-target " "\fItarget\fP"
++.br
++Specifies the standard target. After marking the frame, the rule
++still has to give a standard target so
++.B ebtables
++knows what to do.
++The default target is ACCEPT. Making it CONTINUE can let you do other
++things with the frame in other rules of the chain.
++.SS
+ .B redirect
+ The
+ .B redirect
+@@ -535,37 +636,41 @@
+ frame arrived on. This target can only be used in the
+ .BR BROUTING " chain of the " broute " table and the "
+ .BR PREROUTING " chain of the " nat " table."
+-.br
++.TP
+ .BR "--redirect-target " "\fItarget\fP"
+ .br
+ Specifies the standard target. After doing the MAC redirect, the rule
+ still has to give a standard target so
+ .B ebtables
+ knows what to do.
+-The default target is ACCEPT. Making it CONTINUE could let you use 
++The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP in the
+ BROUTING chain will let the frames be routed. RETURN is also allowed. Note
+ that using RETURN in a base chain is not allowed.
++.SS
++.B snat
++The
++.B snat
++target can only be used in the
++.BR POSTROUTING " chain of the " nat " table."
++It specifies that the source mac address has to be changed.
+ .TP
+-.B mark
+-The mark target can be used in every chain of every table. It is possible
+-to use the marking of a frame/packet in both ebtables and iptables, 
+-if the br-nf code is compiled into the kernel. Both put the marking at the
+-same place. So, you can consider this fact as a feature, or as something to
+-watch out for.
++.BR "--to-source " "\fIaddress\fP"
+ .br
+-.BR "--mark-target " "\fItarget\fP"
++The flag
++.B --to-src
++is an alias for this option.
++.TP
++.BR "--snat-target " "\fItarget\fP"
+ .br
+-Specifies the standard target. After marking the frame, the rule
+-still has to give a standard target so
++Specifies the standard target. After doing the snat, the rule still has 
++to give a standard target so
+ .B ebtables
+ knows what to do.
+-The default target is ACCEPT. Making it CONTINUE can let you do other
+-things with the frame in other rules of the chain.
+-.br
+-.BR "--set-mark " "\fIvalue\fP"
+-.br
+-Mark the frame with the specified unsigned value.
++The default target is ACCEPT. Making it CONTINUE could let you use
++multiple target extensions on the same frame. Making it DROP doesn't
++make sense, but you could do that too. RETURN is also allowed. Note
++that using RETURN in a base chain is not allowed.
+ .br
+ .SH FILES
+ .I /etc/ethertypes
+@@ -573,7 +678,9 @@
+ .I EBTABLES_ATOMIC_FILE
+ .SH BUGS
+ This won't work on an architecture with a user32/kernel64 situation like the Sparc64.
+-.SH AUTHOR
+-.IR "" "Bart De Schuymer <" bdschuym@pandora.be >
++.SH MAILINGLISTS
++.I ebtables-user@lists.sourceforge.net
++.br
++.I ebtables-devel@lists.sourceforge.net
+ .SH SEE ALSO
+-.BR iptables "(8), " brctl (8)
++.BR iptables "(8), " brctl "(8), " ifconfig "(8), " route (8)
+--- ebtables-v2.0.2/include/ebtables_u.h	Wed Nov 20 22:05:39 2002
++++ ebtables-v2.0.3/include/ebtables_u.h	Sun Jan 19 12:37:52 2003
+@@ -30,9 +30,9 @@
+ {
+ 	int policy;
+ 	unsigned int nentries;
+-	// counter offset for this chain
++	/* counter offset for this chain */
+ 	unsigned int counter_offset;
+-	// used for udc
++	/* used for udc */
+ 	unsigned int hook_mask;
+ 	char name[EBT_CHAIN_MAXNAMELEN];
+ 	struct ebt_u_entry *entries;
+@@ -42,7 +42,7 @@
+ {
+ 	struct ebt_u_entries *udc;
+ 	struct ebt_u_chain_list *next;
+-	// this is only used internally, in communication.c
++	/* this is only used internally, in communication.c */
+ 	char *kernel_start;
+ };
+ 
+@@ -50,25 +50,29 @@
+ {
+ 	char name[EBT_TABLE_MAXNAMELEN];
+ 	unsigned int valid_hooks;
+-	// nr of rules in the table
++	/* nr of rules in the table */
+ 	unsigned int nentries;
+ 	struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
+-	// user defined chains (udc) list
++	/* user defined chains (udc) list */
+ 	struct ebt_u_chain_list *udc;
+-	// nr of counters userspace expects back
++	/* nr of counters userspace expects back */
+ 	unsigned int num_counters;
+-	// where the kernel will put the old counters
++	/* where the kernel will put the old counters */
+ 	struct ebt_counter *counters;
+-	// can be used e.g. to know if a standard option
+-	// has been specified twice
++	/*
++	 * can be used e.g. to know if a standard option
++	 * has been specified twice
++	 */
+ 	unsigned int flags;
+-	// we stick the specified command (e.g. -A) in here
++	/* we stick the specified command (e.g. -A) in here */
+ 	char command;
+-	// here we stick the hook to do our thing on (can be -1 if unspecified)
++	/*
++	 * here we stick the hook to do our thing on (can be -1 if unspecified)
++	 */
+ 	int selected_hook;
+-	// used for the atomic option
++	/* used for the atomic option */
+ 	char *filename;
+-	// tells what happened to the old rules
++	/* tells what happened to the old rules */
+ 	unsigned short *counterchanges;
+ };
+ 
+@@ -114,7 +118,7 @@
+ struct ebt_u_match
+ {
+ 	char name[EBT_FUNCTION_MAXNAMELEN];
+-	// size of the real match data
++	/* size of the real match data */
+ 	unsigned int size;
+ 	void (*help)(void);
+ 	void (*init)(struct ebt_entry_match *m);
+@@ -129,12 +133,16 @@
+ 	int (*compare)(const struct ebt_entry_match *m1,
+ 	   const struct ebt_entry_match *m2);
+ 	const struct option *extra_ops;
+-	// can be used e.g. to check for multiple occurance of the same option
++	/*
++	 * can be used e.g. to check for multiple occurance of the same option
++	 */
+ 	unsigned int flags;
+ 	unsigned int option_offset;
+ 	struct ebt_entry_match *m;
+-	// if used == 1 we no longer have to add it to
+-	// the match chain of the new entry
++	/*
++	 * if used == 1 we no longer have to add it to
++	 * the match chain of the new entry
++	 */
+ 	unsigned int used;
+ 	struct ebt_u_match *next;
+ };
+@@ -204,10 +212,10 @@
+ #define print_bug(format, args...) \
+    __print_bug(__FILE__, __LINE__, format, ##args)
+ #define print_error(format,args...) {printf(format".\n",##args); exit(-1);}
+-#define print_memory() {printf("Ebtables: " __FILE__ " " __FUNCTION__ \
+-   " %d :Out of memory.\n", __LINE__); exit(-1);}
++#define print_memory() {printf("Ebtables: " __FILE__ \
++   " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);}
+ 
+-// used for keeping the rule counters right during rule adds or deletes
++/* used for keeping the rule counters right during rule adds or deletes */
+ #define CNT_NORM 0
+ #define CNT_DEL 1
+ #define CNT_ADD 2
+@@ -215,8 +223,10 @@
+ #define CNT_ZERO 4
+ 
+ extern char *standard_targets[NUM_STANDARD_TARGETS];
+-// Transforms a target string into the right integer,
+-// returns 0 on success.
++/*
++ * Transforms a target string into the right integer,
++ * returns 0 on success.
++ */
+ #define FILL_TARGET(_str, _pos) ({                        \
+ 	int _i, _ret = 0;                                 \
+ 	for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++)     \
+@@ -229,12 +239,12 @@
+ 	_ret;                                             \
+ })
+ 
+-// Transforms the target value to an index into standard_targets[]
++/* Transforms the target value to an index into standard_targets[] */
+ #define TARGET_INDEX(_value) (-_value - 1)
+-// Returns a target string corresponding to the value
++/* Returns a target string corresponding to the value */
+ #define TARGET_NAME(_value) (standard_targets[TARGET_INDEX(_value)])
+-// True if the hook mask denotes that the rule is in a base chain
++/* True if the hook mask denotes that the rule is in a base chain */
+ #define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+-// Clear the bit in the hook_mask that tells if the rule is on a base chain
++/* Clear the bit in the hook_mask that tells if the rule is on a base chain */
+ #define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+ #endif /* EBTABLES_U_H */
diff --git a/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.4.001.diff b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.4.001.diff
new file mode 100644
index 0000000..1c7d932
--- /dev/null
+++ b/userspace/patches/incremental-patches/v2.0/ebtables-v2.0.4.001.diff
@@ -0,0 +1,955 @@
+--- ebtables-v2.0.3/Makefile	Tue Apr  1 22:12:30 2003
++++ ebtables-v2.0.4/Makefile	Mon Apr 21 17:56:21 2003
+@@ -1,7 +1,7 @@
+ # ebtables Makefile
+ 
+ PROGNAME:=ebtables
+-PROGVERSION:=2.0.3
++PROGVERSION:=2.0.4
+ PROGDATE:=April\ 2003
+ 
+ MANDIR?=/usr/local/man
+@@ -14,7 +14,7 @@
+ 
+ KERNEL_INCLUDES?=include/
+ 
+-ETHERTYPESPATH?=/etc/
++ETHERTYPESPATH?=/etc
+ ETHERTYPESFILE:=$(ETHERTYPESPATH)/ethertypes
+ 
+ BINPATH?=/sbin/
+--- ebtables-v2.0.3/ebtables.c	Tue Apr  1 20:08:15 2003
++++ ebtables-v2.0.4/ebtables.c	Sat May 24 23:34:22 2003
+@@ -79,6 +79,7 @@
+ 	{ "Lc"            , no_argument      , 0, 4   },
+ 	{ "Ln"            , no_argument      , 0, 5   },
+ 	{ "Lx"            , no_argument      , 0, 6   },
++	{ "Lmac2"         , no_argument      , 0, 12  },
+ 	{ "zero"          , optional_argument, 0, 'Z' },
+ 	{ "flush"         , optional_argument, 0, 'F' },
+ 	{ "policy"        , required_argument, 0, 'P' },
+@@ -367,6 +368,7 @@
+ 	tables = t;
+ }
+ 
++const char *modprobe = NULL;
+ /*
+  * blatently stolen (again) from iptables.c userspace program
+  * find out where the modprobe utility is located
+@@ -397,7 +399,7 @@
+ 	return NULL;
+ }
+ 
+-int ebtables_insmod(const char *modname, const char *modprobe)
++int ebtables_insmod(const char *modname)
+ {
+ 	char *buf = NULL;
+ 	char *argv[3];
+@@ -465,9 +467,33 @@
+  * we use replace.flags, so we can't use the following values:
+  * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
+  */
+-#define LIST_N 0x04
+-#define LIST_C 0x08
+-#define LIST_X 0x10
++#define LIST_N    0x04
++#define LIST_C    0x08
++#define LIST_X    0x10
++#define LIST_MAC2 0x20
++
++void print_mac(const char *mac)
++{
++	if (replace.flags & LIST_MAC2) {
++		int j;
++		for (j = 0; j < ETH_ALEN; j++)
++			printf("%02x%s", (unsigned char)mac[j],
++				(j==ETH_ALEN-1) ? "" : ":");
++	} else
++		printf("%s", ether_ntoa((struct ether_addr *) mac));
++}
++
++void print_mac_and_mask(const char *mac, const char *mask)
++{
++	char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
++
++	print_mac(mac);
++	if (memcmp(mask, hlpmsk, 6)) {
++		printf("/");
++		print_mac(mask);
++	}
++}
++
+ /*
+  * helper function for list_rules()
+  */
+@@ -535,8 +561,6 @@
+ 			}
+ 		}
+ 		if (hlp->bitmask & EBT_SOURCEMAC) {
+-			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+-
+ 			printf("-s ");
+ 			if (hlp->invflags & EBT_ISOURCE)
+ 				printf("! ");
+@@ -555,19 +579,11 @@
+ 				printf("Broadcast");
+ 				goto endsrc;
+ 			}
+-			printf("%s", ether_ntoa((struct ether_addr *)
+-			   hlp->sourcemac));
+-			if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
+-				printf("/");
+-				printf("%s", ether_ntoa((struct ether_addr *)
+-				   hlp->sourcemsk));
+-			}
++			print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk);
+ endsrc:
+ 			printf(" ");
+ 		}
+ 		if (hlp->bitmask & EBT_DESTMAC) {
+-			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+-
+ 			printf("-d ");
+ 			if (hlp->invflags & EBT_IDEST)
+ 				printf("! ");
+@@ -586,13 +602,7 @@
+ 				printf("Broadcast");
+ 				goto enddst;
+ 			}
+-			printf("%s", ether_ntoa((struct ether_addr *)
+-			   hlp->destmac));
+-			if (memcmp(hlp->destmsk, hlpmsk, 6)) {
+-				printf("/");
+-				printf("%s", ether_ntoa((struct ether_addr *)
+-				   hlp->destmsk));
+-			}
++			print_mac_and_mask(hlp->destmac, hlp->destmsk);
+ enddst:
+ 			printf(" ");
+ 		}
+@@ -1081,7 +1091,7 @@
+ 	/*
+ 	 * handle '-D chain rulenr' command
+ 	 */
+-	if (rule_nr != -1) {
++	if (rule_nr != 0) {
+ 		if (rule_nr > entries->nentries)
+ 			return -1;
+ 		/*
+@@ -1186,12 +1196,12 @@
+ 	struct ebt_u_watcher_list *w_l;
+ 	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
+-	if (rule_nr != -1) { /* command -I */
+-		if (--rule_nr > entries->nentries)
+-			print_error("rule nr too high: %d > %d", rule_nr + 1,
+-			   entries->nentries + 1);
+-	} else
+-		rule_nr = entries->nentries;
++	if (rule_nr <= 0)
++		rule_nr += entries->nentries;
++	else
++		rule_nr--;
++	if (rule_nr > entries->nentries || rule_nr < 0)
++		print_error("The specified rule number is incorrect");
+ 	/*
+ 	 * we're adding one rule
+ 	 */
+@@ -1282,6 +1292,14 @@
+ 	struct ebt_u_entry **u_e, *u_e2;
+ 	struct ebt_u_entries *entries = to_chain(), *entries2;
+ 
++	if (begin < 0)
++		begin += entries->nentries + 1;
++	if (end < 0)
++		end += entries->nentries + 1;
++
++	if (begin < 0 || begin > end || end > entries->nentries)
++		print_error("Sorry, wrong rule numbers");
++
+ 	if ((begin = check_rule_exists(begin)) == -1 ||
+ 	    (end = check_rule_exists(end)) == -1)
+ 		print_error("Sorry, rule does not exist");
+@@ -1535,24 +1553,22 @@
+ 	if (colon) {
+ 		*colon = '\0';
+ 		if (*(colon + 1) == '\0')
+-			*rule_nr_end = -1;
++			*rule_nr_end = -1; /* until the last rule */
+ 		else {
+ 			*rule_nr_end = strtol(colon + 1, &buffer, 10);
+-			if (*buffer != '\0' || *rule_nr_end < 0)
++			if (*buffer != '\0' || *rule_nr_end == 0)
+ 				return -1;
+ 		}
+ 	}
+ 	if (colon == argv)
+-		*rule_nr = 1;
++		*rule_nr = 1; /* beginning with the first rule */
+ 	else {
+ 		*rule_nr = strtol(argv, &buffer, 10);
+-		if (*buffer != '\0' || *rule_nr < 0)
++		if (*buffer != '\0' || *rule_nr == 0)
+ 			return -1;
+ 	}
+ 	if (!colon)
+ 		*rule_nr_end = *rule_nr;
+-	if (*rule_nr_end != -1 && *rule_nr > *rule_nr_end)
+-		return -1;
+ 	return 0;
+ }
+ 
+@@ -1576,7 +1592,7 @@
+ 	*flags |= mask;
+ }
+ 
+-static void get_kernel_table(const char *modprobe)
++static void get_kernel_table()
+ {
+ 	if ( !(table = find_table(replace.name)) )
+ 		print_error("Bad table name");
+@@ -1584,7 +1600,7 @@
+ 	 * get the kernel's information
+ 	 */
+ 	if (get_table(&replace)) {
+-		ebtables_insmod("ebtables", modprobe);
++		ebtables_insmod("ebtables");
+ 		if (get_table(&replace))
+ 			print_error("The kernel doesn't support the ebtables "
+ 			"%s table", replace.name);
+@@ -1620,15 +1636,14 @@
+ 	 */
+ 	int zerochain = -1;
+ 	int policy = 0;
+-	int rule_nr = -1; /* used for -[D,I] */
+-	int rule_nr_end = -1; /* used for -I */
++	int rule_nr = 0; /* used for -[D,I] */
++	int rule_nr_end = 0; /* used for -I */
+ 	struct ebt_u_target *t;
+ 	struct ebt_u_match *m;
+ 	struct ebt_u_watcher *w;
+ 	struct ebt_u_match_list *m_l;
+ 	struct ebt_u_watcher_list *w_l;
+ 	struct ebt_u_entries *entries;
+-	const char *modprobe = NULL;
+ 
+ 	opterr = 0;
+ 
+@@ -1674,7 +1689,7 @@
+ 			if (replace.flags & OPT_COMMAND)
+ 				print_error("Multiple commands not allowed");
+ 			replace.flags |= OPT_COMMAND;
+-			get_kernel_table(modprobe);
++			get_kernel_table();
+ 			if (optarg[0] == '-' || !strcmp(optarg, "!"))
+ 				print_error("No chain name specified");
+ 			if (c == 'N') {
+@@ -1758,7 +1773,8 @@
+ 			}
+ 
+ 			if (c == 'D' && optind < argc &&
+-			    argv[optind][0] != '-') {
++			    (argv[optind][0] != '-' ||
++			    (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
+ 				if (parse_delete_rule(argv[optind],
+ 				    &rule_nr, &rule_nr_end))
+ 					print_error("Problem with the "
+@@ -1766,11 +1782,12 @@
+ 				optind++;
+ 			}
+ 			if (c == 'I') {
+-				if (optind >= argc || argv[optind][0] == '-')
++				if (optind >= argc || (argv[optind][0] == '-' &&
++				    (argv[optind][1] < '0' || argv[optind][1] > '9')))
+ 					print_error("No rulenr for -I"
+ 					            " specified");
+ 				rule_nr = strtol(argv[optind], &buffer, 10);
+-				if (*buffer != '\0' || rule_nr < 0)
++				if (*buffer != '\0')
+ 					print_error("Problem with the "
+ 					            "specified rule number");
+ 				optind++;
+@@ -1812,7 +1829,7 @@
+ 					            " not allowed");
+ 				replace.flags |= OPT_COMMAND;
+ 			}
+-			get_kernel_table(modprobe);
++			get_kernel_table();
+ 			i = -1;
+ 			if (optarg) {
+ 				if ( (i = get_hooknr(optarg)) == -1 )
+@@ -2108,6 +2125,12 @@
+ 				print_error("--Lx not compatible with --Ln");
+ 			replace.flags |= LIST_X;
+ 			break;
++		case 12 : /* Lmac2 */
++			check_option(&replace.flags, LIST_MAC2);
++			if (replace.command != 'L')
++				print_error("Use --Lmac2 with -L");
++			replace.flags |= LIST_MAC2;
++			break;
+ 		case 8 : /* atomic-commit */
+ 			replace.command = c;
+ 			if (replace.flags & OPT_COMMAND)
+@@ -2136,7 +2159,6 @@
+ 			 * possible memory leak here
+ 			 */
+ 			replace.filename = NULL;
+-			ebtables_insmod("ebtables", modprobe);
+ 			break;
+ 		case 7 : /* atomic-init */
+ 		case 10: /* atomic-save */
+@@ -2153,7 +2175,7 @@
+ 				tmp = replace.filename;
+ 				/* get the kernel table */
+ 				replace.filename = NULL;
+-				get_kernel_table(modprobe);
++				get_kernel_table();
+ 				replace.filename = tmp;
+ 			}
+ 			if (replace.nentries) {
+@@ -2320,11 +2342,8 @@
+ 				e = e->next;
+ 			}
+ 		}
+-	} else if (replace.command == 'D') {
+-		if (rule_nr != -1 && rule_nr_end == -1)
+-			rule_nr_end = entries->nentries;
++	} else if (replace.command == 'D')
+ 		delete_rule(rule_nr, rule_nr_end);
+-	}
+ 	/*
+ 	 * commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+ 	 * --init-table fall through
+--- ebtables-v2.0.3/communication.c	Tue Apr  1 20:08:23 2003
++++ ebtables-v2.0.4/communication.c	Sun May  4 18:54:44 2003
+@@ -263,10 +263,19 @@
+ 	/* give the data to the kernel */
+ 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+ 	get_sockfd();
+-	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+-		print_error("The kernel doesn't support a certain ebtables"
+-		  " extension, consider recompiling your kernel or insmod"
+-		  " the extension");
++	if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
++		return;
++	if (u_repl->command == 8) { /* the ebtables module may not
++	                            * yet be loaded with --atomic-commit */
++		ebtables_insmod("ebtables");
++		if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES,
++		    repl, optlen))
++			return;
++	}
++
++	print_error("The kernel doesn't support a certain ebtables"
++		    " extension, consider recompiling your kernel or insmod"
++		    " the extension");
+ }
+ 
+ static void store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+@@ -323,6 +332,7 @@
+ 			 * So, we just copy
+ 			 */
+ 			new->pcnt = old->pcnt;
++			new->bcnt = old->bcnt;
+ 			/* we've used an old counter */
+ 			old++;
+ 			/* we've set a new counter */
+--- ebtables-v2.0.3/extensions/ebt_arp.c	Fri Jan 10 00:20:17 2003
++++ ebtables-v2.0.4/extensions/ebt_arp.c	Sun May 25 11:58:48 2003
+@@ -4,6 +4,7 @@
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include "../include/ethernetdb.h"
++#include <linux/if_ether.h>
+ #include <linux/netfilter_bridge/ebt_arp.h>
+ 
+ #define ARP_OPCODE '1'
+@@ -11,6 +12,8 @@
+ #define ARP_PTYPE  '3'
+ #define ARP_IP_S   '4'
+ #define ARP_IP_D   '5'
++#define ARP_MAC_S  '6'
++#define ARP_MAC_D  '7'
+ static struct option opts[] =
+ {
+ 	{ "arp-opcode"    , required_argument, 0, ARP_OPCODE },
+@@ -19,6 +22,8 @@
+ 	{ "arp-ptype"     , required_argument, 0, ARP_PTYPE  },
+ 	{ "arp-ip-src"    , required_argument, 0, ARP_IP_S   },
+ 	{ "arp-ip-dst"    , required_argument, 0, ARP_IP_D   },
++	{ "arp-mac-src"   , required_argument, 0, ARP_MAC_S  },
++	{ "arp-mac-dst"   , required_argument, 0, ARP_MAC_D  },
+ 	{ 0 }
+ };
+ 
+@@ -43,14 +48,16 @@
+ 
+ 	printf(
+ "arp options:\n"
+-"--arp-opcode opcode            : ARP opcode (integer or string)\n"
+-"--arp-htype type               : ARP hardware type (integer or string)\n"
+-"--arp-ptype type               : ARP protocol type (hexadecimal or string)\n"
+-"--arp-ip-src [!] address[/mask]: ARP IP source specification\n"
+-"--arp-ip-dst [!] address[/mask]: ARP IP target specification\n"
++"--arp-opcode opcode             : ARP opcode (integer or string)\n"
++"--arp-htype type                : ARP hardware type (integer or string)\n"
++"--arp-ptype type                : ARP protocol type (hexadecimal or string)\n"
++"--arp-ip-src  [!] address[/mask]: ARP IP source specification\n"
++"--arp-ip-dst  [!] address[/mask]: ARP IP target specification\n"
++"--arp-mac-src [!] address[/mask]: ARP MAC source specification\n"
++"--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n"
+ " opcode strings: \n");
+ 	for (i = 0; i < NUMOPCODES; i++)
+-		printf("%d = %s\n", i + 1, opcodes[i]);
++		printf(" %d = %s\n", i + 1, opcodes[i]);
+ 	printf(
+ " hardware type string: 1 = Ethernet\n"
+ " protocol type string: see "_PATH_ETHERTYPES"\n");
+@@ -67,11 +74,16 @@
+ /* defined in ebt_ip.c */
+ void parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+ 
++/* defined in ebtables.c */
++int getmac_and_mask(char *from, char *to, char *mask);
++
+ #define OPT_OPCODE 0x01
+ #define OPT_HTYPE  0x02
+ #define OPT_PTYPE  0x04
+ #define OPT_IP_S   0x08
+ #define OPT_IP_D   0x10
++#define OPT_MAC_S  0x20
++#define OPT_MAC_D  0x40
+ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+    unsigned int *flags, struct ebt_entry_match **match)
+ {
+@@ -80,6 +92,8 @@
+ 	char *end;
+ 	uint32_t *addr;
+ 	uint32_t *mask;
++	char *maddr;
++	char *mmask;
+ 
+ 	switch (c) {
+ 	case ARP_OPCODE:
+@@ -172,6 +186,32 @@
+ 			print_error("Missing ARP IP address argument");
+ 		parse_ip_address(argv[optind - 1], addr, mask);
+ 		break;
++
++	case ARP_MAC_S:
++	case ARP_MAC_D:
++		if (c == ARP_MAC_S) {
++			check_option(flags, OPT_MAC_S);
++			maddr = arpinfo->smaddr;
++			mmask = arpinfo->smmsk;
++			arpinfo->bitmask |= EBT_ARP_SRC_MAC;
++		} else {
++			check_option(flags, OPT_MAC_D);
++			maddr = arpinfo->dmaddr;
++			mmask = arpinfo->dmmsk;
++			arpinfo->bitmask |= EBT_ARP_DST_MAC;
++		}
++		if (check_inverse(optarg)) {
++			if (c == ARP_MAC_S)
++				arpinfo->invflags |= EBT_ARP_SRC_MAC;
++			else
++				arpinfo->invflags |= EBT_ARP_DST_MAC;
++		}
++		if (optind > argc)
++			print_error("Missing ARP MAC address argument");
++		if (getmac_and_mask(argv[optind - 1], maddr, mmask))
++			print_error("Problem with ARP MAC address argument");
++		break;
++
+ 	default:
+ 		return 0;
+ 	}
+@@ -243,6 +283,20 @@
+ 			   (i == 3) ? "" : ".");
+ 		printf("%s ", mask_to_dotted(arpinfo->dmsk));
+ 	}
++	if (arpinfo->bitmask & EBT_ARP_SRC_MAC) {
++		printf("--arp-mac-src ");
++		if (arpinfo->invflags & EBT_ARP_SRC_MAC)
++			printf("! ");
++		print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk);
++		printf(" ");
++	}
++	if (arpinfo->bitmask & EBT_ARP_DST_MAC) {
++		printf("--arp-mac-dst ");
++		if (arpinfo->invflags & EBT_ARP_DST_MAC)
++			printf("! ");
++		print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk);
++		printf(" ");
++	}
+ }
+ 
+ static int compare(const struct ebt_entry_match *m1,
+@@ -277,6 +331,18 @@
+ 		if (arpinfo1->daddr != arpinfo2->daddr)
+ 			return 0;
+ 		if (arpinfo1->dmsk != arpinfo2->dmsk)
++			return 0;
++	}
++	if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) {
++		if (arpinfo1->smaddr != arpinfo2->smaddr)
++			return 0;
++		if (arpinfo1->smmsk != arpinfo2->smmsk)
++			return 0;
++	}
++	if (arpinfo1->bitmask & EBT_ARP_DST_MAC) {
++		if (arpinfo1->dmaddr != arpinfo2->dmaddr)
++			return 0;
++		if (arpinfo1->dmmsk != arpinfo2->dmmsk)
+ 			return 0;
+ 	}
+ 	return 1;
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0.4/extensions/ebt_pkttype.c	Thu May  1 13:59:15 2003
+@@ -0,0 +1,136 @@
++/*
++ *  ebtables ebt_pkttype
++ *
++ *  Authors:
++ *   Bart De Schuymer <bdschuym@pandora.be>
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <getopt.h>
++#include <netdb.h>
++#include "../include/ebtables_u.h"
++#include <linux/if_packet.h>
++#include <linux/netfilter_bridge/ebt_pkttype.h>
++
++char *classes[] =
++{
++	"host",
++	"broadcast",
++	"multicast",
++	"otherhost",
++	"outgoing",
++	"loopback",
++	"fastroute",
++	"\0"
++};
++
++static struct option opts[] =
++{
++	{ "pkttype-type"        , required_argument, 0, '1' },
++	{ 0 }
++};
++
++static void print_help()
++{
++	printf(
++"pkttype options:\n"
++"--pkttype-type    [!] type: class the packet belongs to\n"
++"Possible values: broadcast, multicast, host, otherhost any byte value.\n");
++}
++
++static void init(struct ebt_entry_match *match)
++{
++	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
++
++	pt->invert = 0;
++}
++
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_match **match)
++{
++	struct ebt_pkttype_info *ptinfo = (struct ebt_pkttype_info *)(*match)->data;
++	char *end;
++	long int i;
++
++	switch (c) {
++	case '1':
++		check_option(flags, 1);
++		if (check_inverse(optarg))
++			ptinfo->invert = 1;
++		if (optind > argc)
++			print_error("Missing pkttype class specification");
++
++		i = strtol(argv[optind - 1], &end, 16);
++		if (*end != '\0') {
++			int j = 0;
++			i = -1;
++			while (classes[j][0])
++				if (!strcasecmp(argv[optind - 1], classes[j++])) {
++					i = j - 1;
++					break;
++				}
++		}
++		if (i < 0 || i > 255)
++			print_error("Problem with specified pkttype class");
++		ptinfo->pkt_type = (uint8_t)i;
++
++		break;
++	default:
++		return 0;
++	}
++	return 1;
++}
++
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match, const char *name,
++   unsigned int hookmask, unsigned int time)
++{
++}
++
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match)
++{
++	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
++	int i = 0;
++
++	printf("--pkttype-type %s", pt->invert ? "! " : "");
++	while (classes[i++][0]);
++	if (pt->pkt_type < i - 1)
++		printf("%s ", classes[pt->pkt_type]);
++	else
++		printf("%d ", pt->pkt_type);
++}
++
++static int compare(const struct ebt_entry_match *m1,
++   const struct ebt_entry_match *m2)
++{
++	struct ebt_pkttype_info *pt1 = (struct ebt_pkttype_info *)m1->data;
++	struct ebt_pkttype_info *pt2 = (struct ebt_pkttype_info *)m2->data;
++
++	if (pt1->invert != pt2->invert ||
++	    pt1->pkt_type != pt2->pkt_type)
++		return 0;
++	return 1;
++}
++
++static struct ebt_u_match pkttype_match =
++{
++	EBT_PKTTYPE_MATCH,
++	sizeof(struct ebt_pkttype_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts
++};
++
++static void _init(void) __attribute((constructor));
++static void _init(void)
++{
++	register_match(&pkttype_match);
++}
+--- /dev/null	Thu Aug 24 11:00:32 2000
++++ ebtables-v2.0.4/extensions/ebt_802_3.c	Mon May 12 19:08:29 2003
+@@ -0,0 +1,145 @@
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <getopt.h>
++#include "../include/ebtables_u.h"
++#include "../include/ethernetdb.h"
++#include <linux/netfilter_bridge/ebt_802_3.h>
++
++#define _802_3_SAP '1'
++#define _802_3_TYPE '2'
++
++static struct option opts[] =
++{
++	{ "802_3-sap"   , required_argument, 0, _802_3_SAP },
++	{ "802_3-type"  , required_argument, 0, _802_3_TYPE },
++	{ 0 }
++};
++
++static void print_help()
++{
++	printf(
++"802_3 options:\n"
++"--802_3-sap [!] protocol       : 802.3 DSAP/SSAP- 1 byte value (hex)\n"
++"  DSAP and SSAP are always the same.  One SAP applies to both fields\n"
++"--802_3-type [!] protocol      : 802.3 SNAP Type- 2 byte value (hex)\n"
++"  Type implies SAP value 0xaa\n");
++}
++
++static void init(struct ebt_entry_match *match)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
++
++	info->invflags = 0;
++	info->bitmask = 0;
++}
++
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++   unsigned int *flags, struct ebt_entry_match **match)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data;
++	unsigned int i;
++	char *end;
++
++	switch (c) {
++		case _802_3_SAP:
++			check_option(flags, _802_3_SAP);
++			if (check_inverse(optarg))
++				info->invflags |= EBT_802_3_SAP;
++
++			if (optind > argc)
++				print_error("Missing 802.3-sap argument");
++			i = strtoul(argv[optind - 1], &end, 16);
++			if (i > 255 || *end != '\0') 
++				print_error("Problem with specified "
++				            "sap hex value, %x",i);
++			info->sap = i; /* one byte, so no byte order worries */
++			info->bitmask |= EBT_802_3_SAP;
++			break;
++		case _802_3_TYPE:
++			check_option(flags, _802_3_TYPE);
++			if (check_inverse(optarg))
++				info->invflags |= EBT_802_3_TYPE;
++			if (optind > argc)
++				print_error("Missing 802.3-type argument");
++			i = strtoul(argv[optind - 1], &end, 16);
++			if (i > 65535 || *end != '\0') {
++				print_error("Problem with the specified "
++					    "type hex value, %x",i);
++			}
++			info->type = htons(i);
++			info->bitmask |= EBT_802_3_TYPE;
++			break;
++		default:
++			return 0;
++	}
++	return 1;
++}
++
++static void final_check(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match, const char *name,
++   unsigned int hookmask, unsigned int time)
++{
++	if (!(entry->bitmask & EBT_802_3))
++		print_error("For 802.3 DSAP/SSAP filtering the protocol "
++			    "must be LENGTH");
++}
++
++static void print(const struct ebt_u_entry *entry,
++   const struct ebt_entry_match *match)
++{
++	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
++
++	if (info->bitmask & EBT_802_3_SAP) {
++		printf("--802_3-sap ");
++		if (info->invflags & EBT_802_3_SAP)
++			printf("! ");
++		printf("0x%.2x ", info->sap);
++	}
++	if (info->bitmask & EBT_802_3_TYPE) {
++		printf("--802_3-type ");
++		if (info->invflags & EBT_802_3_TYPE)
++			printf("! ");
++		printf("0x%.4x ", ntohs(info->type));
++	}
++}
++
++static int compare(const struct ebt_entry_match *m1,
++   const struct ebt_entry_match *m2)
++{
++	struct ebt_802_3_info *info1 = (struct ebt_802_3_info *)m1->data;
++	struct ebt_802_3_info *info2 = (struct ebt_802_3_info *)m2->data;
++
++	if (info1->bitmask != info2->bitmask)
++		return 0;
++	if (info1->invflags != info2->invflags)
++		return 0;
++	if (info1->bitmask & EBT_802_3_SAP) {
++		if (info1->sap != info2->sap) 
++			return 0;
++	}
++	if (info1->bitmask & EBT_802_3_TYPE) {
++		if (info1->type != info2->type)
++			return 0;
++	}
++	return 1;
++}
++
++static struct ebt_u_match _802_3_match = 
++{
++	EBT_802_3_MATCH,
++	sizeof(struct ebt_802_3_info),
++	print_help,
++	init,
++	parse,
++	final_check,
++	print,
++	compare,
++	opts
++};
++
++static void _init(void) __attribute__ ((constructor));
++static void _init(void)
++{
++	        register_match(&_802_3_match);
++}
+--- ebtables-v2.0.3/extensions/Makefile	Fri Nov 22 20:44:37 2002
++++ ebtables-v2.0.4/extensions/Makefile	Mon May 12 18:55:42 2003
+@@ -1,6 +1,6 @@
+ #! /usr/bin/make
+ 
+-EXT_FUNC+=nat arp ip standard log redirect vlan mark_m mark
++EXT_FUNC+=802_3 nat arp ip standard log redirect vlan mark_m mark pkttype
+ EXT_TABLES+=filter nat broute
+ EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+ EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+--- ebtables-v2.0.3/ChangeLog	Tue Apr  1 20:07:24 2003
++++ ebtables-v2.0.4/ChangeLog	Sun Jun  1 18:50:53 2003
+@@ -1,3 +1,14 @@
++20030601
++	* added --Lmac2
++	* <csv_at_bluetail.com> Chris Vitale: basic 802.3/802.2 filtering
++	  (experimental, kernel files are in the CVS)
++
++20030503
++	* added negative rule counter support
++	* bugfix: bcnt was not updated correctly
++	* <blancher_at_cartel-securite.fr> Cedric Blancher: add ARP MAC
++	  matching support
++	* added pkttype match
+ 20030402
+ 	* fixed check bug in ebt_ip.c (report from
+ 	  joe_judge_at_guardium.com).
+--- ebtables-v2.0.3/ebtables.8	Tue Apr  1 20:15:04 2003
++++ ebtables-v2.0.4/ebtables.8	Sat May  3 23:16:34 2003
+@@ -26,27 +26,27 @@
+ .SH NAME
+ ebtables (v.2.0) \- Ethernet bridge frame table administration
+ .SH SYNOPSIS
+-.BR "ebtables [-t table] -[ADI] " "chain rule-specification [match-extensions] [watcher-extensions] TARGET"
++.BR "ebtables " [ "-t table" ] " -" [ ADI ] " chain rule-specification " [ match-extensions "] [" watcher-extensions ] " TARGET"
+ .br
+-.BR "ebtables [-t table] -P " "chain " "ACCEPT | DROP | RETURN"
++.BR "ebtables " [ "-t table" ] " -P chain ACCEPT " | " DROP " | " RETURN"
+ .br
+-.BR "ebtables [-t table] -F [" "chain" "]"
++.BR "ebtables " [ "-t table" ] " -F " [ chain ]
+ .br
+-.BR "ebtables [-t table] -Z [" "chain" "]"
++.BR "ebtables " [ "-t table" ] " -Z " [ chain ]
+ .br
+-.BR "ebtables [-t table] -L [-Z] [" chain "] [ [" --Ln "] [" --Lc "] ] " | " [" --Lx "]"
++.BR "ebtables " [ "-t table" ] " -L " [ -Z "] [" " chain" "] [ [ [" --Ln "] [" --Lc "] ] | [" --Lx "] ] [" --Lmac2 "]"
+ .br
+-.BR "ebtables [-t table] -[NX] " chain
++.BR "ebtables " [ "-t table" ] " -" [ NX ] " chain"
+ .br
+-.BR "ebtables [-t table] -E " "old-chain-name new-chain-name"
++.BR "ebtables " [ "-t table" ] " -E old-chain-name new-chain-name"
+ .br
+-.BR "ebtables [-t table] --init-table"
++.BR "ebtables " [ "-t table" ] " --init-table"
+ .br
+-.BR "ebtables [-t table] [--atomic-file file] --atomic-commit
++.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-commit"
+ .br
+-.BR "ebtables [-t table] [--atomic-file file] --atomic-init"
++.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-init"
+ .br
+-.BR "ebtables [-t table] [--atomic-file file] --atomic-save"
++.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-save"
+ .br
+ .SH DESCRIPTION
+ .B ebtables
+@@ -171,12 +171,17 @@
+ .B "-D, --delete"
+ Delete the specified rule from the selected chain. There are two ways to
+ use this command. The first is by specifying an interval of rule numbers
+-to delete, syntax: start_nr[:end_nr]. The second usage is by specifying
+-the complete rule as it would have been specified when it was added.
++to delete, syntax: start_nr[:end_nr]. Using negative numbers is allowed, for more
++details about using negative numbers, see the -I command. The second usage is by
++specifying the complete rule as it would have been specified when it was added.
+ .TP
+ .B "-I, --insert"
+ Insert the specified rule into the selected chain at the specified rule number.
+-The number one, 1, means the head of the chain.
++If the current number of rules equals N, then the specified number can be
++between -N and N+1. For a positive number i, it holds that i and i-N-1 specify the
++same place in the chain where the rule should be inserted. The number 0 specifies
++the place past the last rule in the chain and using this number is therefore
++equivalent with using the -A command.
+ .TP
+ .B "-P, --policy"
+ Set the policy for the chain to the given target. The policy can be
+@@ -235,8 +240,14 @@
+ .B "--Ln"
+ and
+ .B "--Lc"
+-chain listing options, 
+-.B "-L."
++chain listing options.
++.br
++.B "--Lmac2"
++.br
++Shows all MAC addresses with the same length, adding leading zeroes
++if necessary. The default representation omits zeroes in the addresses
++when they are not needed.
++.br
+ All necessary
+ .B ebtables
+ commands for making the current list of
+@@ -247,7 +258,7 @@
+ supplied for the
+ .B "-L"
+ command while using the
+-.B "-Lx"
++.B "--Lx"
+ option.
+ .TP
+ .B "-N, --new-chain"
+@@ -483,6 +494,12 @@
+ .TP
+ .BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+ The ARP IP destination address specification.
++.TP
++.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP MAC source address specification.
++.TP
++.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]"
++The ARP MAC destination address specification.
+ .SS ip
+ Specify ip fields. These will only work if the protocol equals
+ .BR IPv4 .
+@@ -530,6 +547,14 @@
+ mark value. If only a mask is specified (start with '/') the logical AND
+ of the mark value of the frame and the user-specified mark is taken and
+ the result is compared with zero.
++.SS pkttype
++.TP
++.BR "--pkttype-type " "[!] \fItype\fP"
++Matches on the Ethernet "class" of the frame, which is determined by the
++generic networking code. Possible values: broadcast (MAC destination is
++broadcast address), multicast (MAC destination is multicast address),
++host (MAC destination is the receiving network device) or otherhost
++(none of the above).
+ .SS vlan
+ Specify 802.1Q Tag Control Information fields.
+ The protocol rule specification (frame type) should be set to
+--- ebtables-v2.0.3/include/ebtables_u.h	Sun Jan 19 12:37:52 2003
++++ ebtables-v2.0.4/include/ebtables_u.h	Sun May  4 18:51:43 2003
+@@ -208,10 +208,14 @@
+ void deliver_table(struct ebt_u_replace *repl);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
++void print_mac(const char *mac);
++void print_mac_and_mask(const char *mac, const char *mask);
++int ebtables_insmod(const char *modname);
+ void __print_bug(char *file, int line, char *format, ...);
+ #define print_bug(format, args...) \
+    __print_bug(__FILE__, __LINE__, format, ##args)
+-#define print_error(format,args...) {printf(format".\n",##args); exit(-1);}
++#define print_error(format,args...) {printf(format,##args);\
++   printf(".\n");exit(-1);}
+ #define print_memory() {printf("Ebtables: " __FILE__ \
+    " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);}
+ 
diff --git a/userspace/patches/zipped/ebtables-v2.0-rc1.tar.gz b/userspace/patches/zipped/ebtables-v2.0-rc1.tar.gz
new file mode 100644
index 0000000..030c57e
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0-rc1.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0-rc2.tar.gz b/userspace/patches/zipped/ebtables-v2.0-rc2.tar.gz
new file mode 100644
index 0000000..371864c
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0-rc2.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0.tar.gz b/userspace/patches/zipped/ebtables-v2.0.tar.gz
new file mode 100644
index 0000000..adf9a38
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre1.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre1.tar.gz
new file mode 100644
index 0000000..52322df
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre1.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre10.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre10.tar.gz
new file mode 100644
index 0000000..9bb2073
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre10.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre2.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre2.tar.gz
new file mode 100644
index 0000000..e398d7c
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre2.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre3.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre3.tar.gz
new file mode 100644
index 0000000..d27fac2
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre3.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre4.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre4.tar.gz
new file mode 100644
index 0000000..7fc57ce
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre4.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre5.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre5.tar.gz
new file mode 100644
index 0000000..c09e318
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre5.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre6.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre6.tar.gz
new file mode 100644
index 0000000..dd0fedc
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre6.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre7.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre7.tar.gz
new file mode 100644
index 0000000..58c7af7
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre7.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz
new file mode 100644
index 0000000..9dcd7c5
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/ebtables-v2.0pre9.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre9.tar.gz
new file mode 100644
index 0000000..96f6bfc
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre9.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/v2.0/ebtables-v2.0.1.tar.gz b/userspace/patches/zipped/v2.0/ebtables-v2.0.1.tar.gz
new file mode 100644
index 0000000..03fd6ac
--- /dev/null
+++ b/userspace/patches/zipped/v2.0/ebtables-v2.0.1.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/v2.0/ebtables-v2.0.2.tar.gz b/userspace/patches/zipped/v2.0/ebtables-v2.0.2.tar.gz
new file mode 100644
index 0000000..25ae298
--- /dev/null
+++ b/userspace/patches/zipped/v2.0/ebtables-v2.0.2.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/v2.0/ebtables-v2.0.3.tar.gz b/userspace/patches/zipped/v2.0/ebtables-v2.0.3.tar.gz
new file mode 100644
index 0000000..7113e83
--- /dev/null
+++ b/userspace/patches/zipped/v2.0/ebtables-v2.0.3.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/v2.0/ebtables-v2.0.4.tar.gz b/userspace/patches/zipped/v2.0/ebtables-v2.0.4.tar.gz
new file mode 100644
index 0000000..e0efcce
--- /dev/null
+++ b/userspace/patches/zipped/v2.0/ebtables-v2.0.4.tar.gz
Binary files differ
diff --git a/userspace/patches/zipped/v2.0/ebtables-v2.0.6.tar.gz b/userspace/patches/zipped/v2.0/ebtables-v2.0.6.tar.gz
new file mode 100644
index 0000000..6c69082
--- /dev/null
+++ b/userspace/patches/zipped/v2.0/ebtables-v2.0.6.tar.gz
Binary files differ
diff --git a/userspace/scripts/Make_userspace_diff b/userspace/scripts/Make_userspace_diff
new file mode 100755
index 0000000..f627c7b
--- /dev/null
+++ b/userspace/scripts/Make_userspace_diff
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Used to make incremental diffs for ebtables userspace
+# 31 July 2002, added mark match/target
+
+export FROM=/ebtables-cvs/ebtables2/userspace/ebtables2/
+export TO=ebtables-v2.0.2
+export FILE=iets.diff
+
+diff -urN $FROM/Makefile $TO/Makefile > $FILE
+diff -urN $FROM/ebtables.c $TO/ebtables.c >> $FILE
+diff -urN $FROM/getethertype.c $TO/getethertype.c >> $FILE
+diff -urN $FROM/communication.c $TO/communication.c >> $FILE
+diff -urN $FROM/extensions/ebt_redirect.c $TO/extensions/ebt_redirect.c >> $FILE
+diff -urN $FROM/extensions/ebtable_broute.c $TO/extensions/ebtable_broute.c >> $FILE
+diff -urN $FROM/extensions/ebt_nat.c $TO/extensions/ebt_nat.c >> $FILE
+diff -urN $FROM/extensions/ebt_ip.c $TO/extensions/ebt_ip.c >> $FILE
+diff -urN $FROM/extensions/ebt_arp.c $TO/extensions/ebt_arp.c >> $FILE
+diff -urN $FROM/extensions/ebt_vlan.c $TO/extensions/ebt_vlan.c >> $FILE
+diff -urN $FROM/extensions/ebt_log.c $TO/extensions/ebt_log.c >> $FILE
+diff -urN $FROM/extensions/ebt_standard.c $TO/extensions/ebt_standard.c >> $FILE
+diff -urN $FROM/extensions/ebtable_filter.c $TO/extensions/ebtable_filter.c >> $FILE
+diff -urN $FROM/extensions/ebtable_nat.c $TO/extensions/ebtable_nat.c >> $FILE
+diff -urN $FROM/extensions/ebt_mark.c $TO/extensions/ebt_mark.c >> $FILE
+diff -urN $FROM/extensions/ebt_mark_m.c $TO/extensions/ebt_mark_m.c >> $FILE
+diff -urN $FROM/extensions/Makefile $TO/extensions/Makefile >> $FILE
+diff -urN $FROM/COPYING $TO/COPYING >> $FILE
+diff -urN $FROM/THANKS $TO/THANKS >> $FILE
+diff -urN $FROM/ChangeLog $TO/ChangeLog >> $FILE
+diff -urN $FROM/ebtables.8 $TO/ebtables.8 >> $FILE
+diff -urN $FROM/ethertypes $TO/ethertypes >> $FILE
+diff -urN $FROM/include/ebtables_u.h $TO/include/ebtables_u.h >> $FILE
+diff -urN $FROM/include/ethernetdb.h $TO/include/ethernetdb.h >> $FILE
diff --git a/userspace/scripts/diffUser b/userspace/scripts/diffUser
new file mode 100755
index 0000000..d5541ac
--- /dev/null
+++ b/userspace/scripts/diffUser
@@ -0,0 +1,16 @@
+#!/bin/bash
+#This needs to be executed from within the work version
+currdir=.
+olddir=../ebtables2/userspace/ebtables2
+regexp="find $currdir -name \"*.c\";"
+regexp=$regexp"find $currdir -name \"*.h\";find $currdir -name \"Makefile\";"
+regexp=$regexp"find $currdir -name \"ethertypes\";find $currdir -name \"ChangeLog\";"
+regexp=$regexp"find $currdir -name \"COPYING\";find $currdir -name \"INSTALL\";"
+regexp=$regexp"find $currdir -name \"THANKS\";find $currdir -name \"perf_test\" -type f;"
+regexp=$regexp"find $currdir -name \"*.8\";find $currdir -name \"ebtables-save\";"
+regexp=$regexp"find $currdir -name \"ebtables-config\";find $currdir -name \"*sysv\";"
+regexp=$regexp"find $currdir -name \"ebtables.spec\";"
+for a in `eval $regexp`
+do
+  diff -purN $olddir/$a $currdir/$a
+done
\ No newline at end of file
diff --git a/userspace/scripts/mvUser b/userspace/scripts/mvUser
new file mode 100755
index 0000000..9ba0e3e
--- /dev/null
+++ b/userspace/scripts/mvUser
@@ -0,0 +1,24 @@
+#!/bin/bash
+#This needs to be executed from within the destination directory
+currdir=$PWD
+olddir=$PWD/../ebtables2/userspace/ebtables2
+cd $olddir
+regexp="find . -name \"*.c\";"
+regexp=$regexp"find . -name \"*.h\";find . -name \"Makefile\";"
+regexp=$regexp"find . -name \"ethertypes\";find . -name \"ChangeLog\";"
+regexp=$regexp"find . -name \"COPYING\";find . -name \"INSTALL\";"
+regexp=$regexp"find . -name \"THANKS\";find . -name \"perf_test\" -type f;"
+regexp=$regexp"find . -name \"*.8\";find . -name \"ebtables-save\";"
+regexp=$regexp"find . -name \"ebtables-config\";find . -name \"*sysv\";"
+regexp=$regexp"find . -name \"ebtables.spec\";"
+cd - > /dev/null
+mkdir -p $currdir/include
+mkdir -p $currdir/examples/perf_test
+mkdir -p $currdir/examples/ulog
+mkdir -p $currdir/extensions
+cd $olddir
+for a in `eval $regexp`
+do
+  install -m 0644 -o $USER -g $USER $olddir/$a $currdir/$a
+done
+cd - > /dev/null